commit
fdc52a0f83
50 changed files with 3146 additions and 670 deletions
|
@ -71,7 +71,7 @@ _* See [here](https://github.com/d5/tengobench) for commands/codes used_
|
|||
- [Builtin Functions](https://github.com/d5/tengo/blob/master/docs/builtins.md)
|
||||
- [Interoperability](https://github.com/d5/tengo/blob/master/docs/interoperability.md)
|
||||
- [Tengo CLI](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md)
|
||||
- [Standard Library](https://github.com/d5/tengo/blob/master/docs/stdlib.md) _(experimental)_
|
||||
- [Standard Library](https://github.com/d5/tengo/blob/master/docs/stdlib.md)
|
||||
|
||||
## Roadmap
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@ import (
|
|||
|
||||
// NoError asserts err is not an error.
|
||||
func NoError(t *testing.T, err error, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
|
@ -26,8 +24,6 @@ func NoError(t *testing.T, err error, msg ...interface{}) bool {
|
|||
|
||||
// Error asserts err is an error.
|
||||
func Error(t *testing.T, err error, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
@ -37,8 +33,6 @@ func Error(t *testing.T, err error, msg ...interface{}) bool {
|
|||
|
||||
// Nil asserts v is nil.
|
||||
func Nil(t *testing.T, v interface{}, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
if v == nil {
|
||||
return true
|
||||
}
|
||||
|
@ -48,8 +42,6 @@ func Nil(t *testing.T, v interface{}, msg ...interface{}) bool {
|
|||
|
||||
// True asserts v is true.
|
||||
func True(t *testing.T, v bool, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
if v {
|
||||
return true
|
||||
}
|
||||
|
@ -59,8 +51,6 @@ func True(t *testing.T, v bool, msg ...interface{}) bool {
|
|||
|
||||
// False asserts vis false.
|
||||
func False(t *testing.T, v bool, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
if !v {
|
||||
return true
|
||||
}
|
||||
|
@ -70,8 +60,6 @@ func False(t *testing.T, v bool, msg ...interface{}) bool {
|
|||
|
||||
// NotNil asserts v is not nil.
|
||||
func NotNil(t *testing.T, v interface{}, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
if v != nil {
|
||||
return true
|
||||
}
|
||||
|
@ -81,8 +69,6 @@ func NotNil(t *testing.T, v interface{}, msg ...interface{}) bool {
|
|||
|
||||
// IsType asserts expected and actual are of the same type.
|
||||
func IsType(t *testing.T, expected, actual interface{}, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
if reflect.TypeOf(expected) == reflect.TypeOf(actual) {
|
||||
return true
|
||||
}
|
||||
|
@ -92,15 +78,13 @@ func IsType(t *testing.T, expected, actual interface{}, msg ...interface{}) bool
|
|||
|
||||
// Equal asserts expected and actual are equal.
|
||||
func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
if expected == nil {
|
||||
return Nil(t, actual, "expected nil, but got not nil")
|
||||
}
|
||||
if !NotNil(t, actual, "expected not nil, but got nil") {
|
||||
return false
|
||||
}
|
||||
if !IsType(t, expected, actual) {
|
||||
if !IsType(t, expected, actual, msg...) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -123,7 +107,7 @@ func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool
|
|||
}
|
||||
case []byte:
|
||||
if bytes.Compare(expected, actual.([]byte)) != 0 {
|
||||
return failExpectedActual(t, expected, actual, msg...)
|
||||
return failExpectedActual(t, string(expected), string(actual.([]byte)), msg...)
|
||||
}
|
||||
case []int:
|
||||
if !equalIntSlice(expected, actual.([]int)) {
|
||||
|
@ -150,39 +134,47 @@ func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool
|
|||
return failExpectedActual(t, expected, actual, msg...)
|
||||
}
|
||||
case []objects.Object:
|
||||
return equalObjectSlice(t, expected, actual.([]objects.Object))
|
||||
return equalObjectSlice(t, expected, actual.([]objects.Object), msg...)
|
||||
case *objects.Int:
|
||||
return Equal(t, expected.Value, actual.(*objects.Int).Value)
|
||||
return Equal(t, expected.Value, actual.(*objects.Int).Value, msg...)
|
||||
case *objects.Float:
|
||||
return Equal(t, expected.Value, actual.(*objects.Float).Value)
|
||||
return Equal(t, expected.Value, actual.(*objects.Float).Value, msg...)
|
||||
case *objects.String:
|
||||
return Equal(t, expected.Value, actual.(*objects.String).Value)
|
||||
return Equal(t, expected.Value, actual.(*objects.String).Value, msg...)
|
||||
case *objects.Char:
|
||||
return Equal(t, expected.Value, actual.(*objects.Char).Value)
|
||||
return Equal(t, expected.Value, actual.(*objects.Char).Value, msg...)
|
||||
case *objects.Bool:
|
||||
return Equal(t, expected.Value, actual.(*objects.Bool).Value)
|
||||
if expected != actual {
|
||||
return failExpectedActual(t, expected, actual, msg...)
|
||||
}
|
||||
case *objects.ReturnValue:
|
||||
return Equal(t, expected.Value, actual.(objects.ReturnValue).Value)
|
||||
return Equal(t, expected.Value, actual.(objects.ReturnValue).Value, msg...)
|
||||
case *objects.Array:
|
||||
return equalObjectSlice(t, expected.Value, actual.(*objects.Array).Value)
|
||||
return equalObjectSlice(t, expected.Value, actual.(*objects.Array).Value, msg...)
|
||||
case *objects.ImmutableArray:
|
||||
return equalObjectSlice(t, expected.Value, actual.(*objects.ImmutableArray).Value)
|
||||
return equalObjectSlice(t, expected.Value, actual.(*objects.ImmutableArray).Value, msg...)
|
||||
case *objects.Bytes:
|
||||
if bytes.Compare(expected.Value, actual.(*objects.Bytes).Value) != 0 {
|
||||
return failExpectedActual(t, expected.Value, actual.(*objects.Bytes).Value, msg...)
|
||||
return failExpectedActual(t, string(expected.Value), string(actual.(*objects.Bytes).Value), msg...)
|
||||
}
|
||||
case *objects.Map:
|
||||
return equalObjectMap(t, expected.Value, actual.(*objects.Map).Value)
|
||||
return equalObjectMap(t, expected.Value, actual.(*objects.Map).Value, msg...)
|
||||
case *objects.ImmutableMap:
|
||||
return equalObjectMap(t, expected.Value, actual.(*objects.ImmutableMap).Value)
|
||||
return equalObjectMap(t, expected.Value, actual.(*objects.ImmutableMap).Value, msg...)
|
||||
case *objects.CompiledFunction:
|
||||
return equalCompiledFunction(t, expected, actual.(*objects.CompiledFunction))
|
||||
return equalCompiledFunction(t, expected, actual.(*objects.CompiledFunction), msg...)
|
||||
case *objects.Closure:
|
||||
return equalClosure(t, expected, actual.(*objects.Closure))
|
||||
return equalClosure(t, expected, actual.(*objects.Closure), msg...)
|
||||
case *objects.Undefined:
|
||||
return true
|
||||
if expected != actual {
|
||||
return failExpectedActual(t, expected, actual, msg...)
|
||||
}
|
||||
case *objects.Error:
|
||||
return Equal(t, expected.Value, actual.(*objects.Error).Value)
|
||||
return Equal(t, expected.Value, actual.(*objects.Error).Value, msg...)
|
||||
case objects.Object:
|
||||
if !expected.Equals(actual.(objects.Object)) {
|
||||
return failExpectedActual(t, expected, actual, msg...)
|
||||
}
|
||||
case error:
|
||||
if expected != actual.(error) {
|
||||
return failExpectedActual(t, expected, actual, msg...)
|
||||
|
@ -196,8 +188,6 @@ func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool
|
|||
|
||||
// Fail marks the function as having failed but continues execution.
|
||||
func Fail(t *testing.T, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
t.Logf("\nError trace:\n\t%s\n%s", strings.Join(errorTrace(), "\n\t"), message(msg...))
|
||||
|
||||
t.Fail()
|
||||
|
@ -206,8 +196,6 @@ func Fail(t *testing.T, msg ...interface{}) bool {
|
|||
}
|
||||
|
||||
func failExpectedActual(t *testing.T, expected, actual interface{}, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
var addMsg string
|
||||
if len(msg) > 0 {
|
||||
addMsg = "\nMessage: " + message(msg...)
|
||||
|
@ -256,15 +244,15 @@ func equalSymbol(a, b compiler.Symbol) bool {
|
|||
a.Scope == b.Scope
|
||||
}
|
||||
|
||||
func equalObjectSlice(t *testing.T, expected, actual []objects.Object) bool {
|
||||
func equalObjectSlice(t *testing.T, expected, actual []objects.Object, msg ...interface{}) bool {
|
||||
// TODO: this test does not differentiate nil vs empty slice
|
||||
|
||||
if !Equal(t, len(expected), len(actual)) {
|
||||
if !Equal(t, len(expected), len(actual), msg...) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(expected); i++ {
|
||||
if !Equal(t, expected[i], actual[i]) {
|
||||
if !Equal(t, expected[i], actual[i], msg...) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -272,15 +260,15 @@ func equalObjectSlice(t *testing.T, expected, actual []objects.Object) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func equalObjectMap(t *testing.T, expected, actual map[string]objects.Object) bool {
|
||||
if !Equal(t, len(expected), len(actual)) {
|
||||
func equalObjectMap(t *testing.T, expected, actual map[string]objects.Object, msg ...interface{}) bool {
|
||||
if !Equal(t, len(expected), len(actual), msg...) {
|
||||
return false
|
||||
}
|
||||
|
||||
for key, expectedVal := range expected {
|
||||
actualVal := actual[key]
|
||||
|
||||
if !Equal(t, expectedVal, actualVal) {
|
||||
if !Equal(t, expectedVal, actualVal, msg...) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -288,27 +276,27 @@ func equalObjectMap(t *testing.T, expected, actual map[string]objects.Object) bo
|
|||
return true
|
||||
}
|
||||
|
||||
func equalCompiledFunction(t *testing.T, expected, actual objects.Object) bool {
|
||||
func equalCompiledFunction(t *testing.T, expected, actual objects.Object, msg ...interface{}) bool {
|
||||
expectedT := expected.(*objects.CompiledFunction)
|
||||
actualT := actual.(*objects.CompiledFunction)
|
||||
|
||||
return Equal(t, expectedT.Instructions, actualT.Instructions)
|
||||
return Equal(t, expectedT.Instructions, actualT.Instructions, msg...)
|
||||
}
|
||||
|
||||
func equalClosure(t *testing.T, expected, actual objects.Object) bool {
|
||||
func equalClosure(t *testing.T, expected, actual objects.Object, msg ...interface{}) bool {
|
||||
expectedT := expected.(*objects.Closure)
|
||||
actualT := actual.(*objects.Closure)
|
||||
|
||||
if !Equal(t, expectedT.Fn, actualT.Fn) {
|
||||
if !Equal(t, expectedT.Fn, actualT.Fn, msg...) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !Equal(t, len(expectedT.Free), len(actualT.Free)) {
|
||||
if !Equal(t, len(expectedT.Free), len(actualT.Free), msg...) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(expectedT.Free); i++ {
|
||||
if !Equal(t, *expectedT.Free[i], *actualT.Free[i]) {
|
||||
if !Equal(t, *expectedT.Free[i], *actualT.Free[i], msg...) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,16 @@ func (b *Bytecode) Decode(r io.Reader) error {
|
|||
return err
|
||||
}
|
||||
|
||||
return dec.Decode(&b.Constants)
|
||||
if err := dec.Decode(&b.Constants); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// replace Bool and Undefined with known value
|
||||
for i, v := range b.Constants {
|
||||
b.Constants[i] = cleanupObjects(v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode writes Bytecode data to the writer.
|
||||
|
@ -32,9 +41,32 @@ func (b *Bytecode) Encode(w io.Writer) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// constants
|
||||
return enc.Encode(b.Constants)
|
||||
}
|
||||
|
||||
func cleanupObjects(o objects.Object) objects.Object {
|
||||
switch o := o.(type) {
|
||||
case *objects.Bool:
|
||||
if o.IsFalsy() {
|
||||
return objects.FalseValue
|
||||
}
|
||||
return objects.TrueValue
|
||||
case *objects.Undefined:
|
||||
return objects.UndefinedValue
|
||||
case *objects.Array:
|
||||
for i, v := range o.Value {
|
||||
o.Value[i] = cleanupObjects(v)
|
||||
}
|
||||
case *objects.Map:
|
||||
for k, v := range o.Value {
|
||||
o.Value[k] = cleanupObjects(v)
|
||||
}
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
func init() {
|
||||
gob.Register(&objects.Int{})
|
||||
gob.Register(&objects.Float{})
|
||||
|
@ -52,4 +84,5 @@ func init() {
|
|||
gob.Register(&objects.StringIterator{})
|
||||
gob.Register(&objects.MapIterator{})
|
||||
gob.Register(&objects.ArrayIterator{})
|
||||
gob.Register(&objects.Time{})
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package compiler_test
|
|||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/d5/tengo/assert"
|
||||
"github.com/d5/tengo/compiler"
|
||||
|
@ -14,16 +15,20 @@ func TestBytecode(t *testing.T) {
|
|||
|
||||
testBytecodeSerialization(t, bytecode(
|
||||
concat(), objectsArray(
|
||||
objects.UndefinedValue,
|
||||
&objects.Time{Value: time.Now()},
|
||||
&objects.Array{
|
||||
Value: objectsArray(
|
||||
&objects.Int{Value: 12},
|
||||
&objects.String{Value: "foo"},
|
||||
&objects.Bool{Value: true},
|
||||
objects.TrueValue,
|
||||
objects.FalseValue,
|
||||
&objects.Float{Value: 93.11},
|
||||
&objects.Char{Value: 'x'},
|
||||
objects.UndefinedValue,
|
||||
),
|
||||
},
|
||||
&objects.Bool{Value: false},
|
||||
objects.FalseValue,
|
||||
&objects.Char{Value: 'y'},
|
||||
&objects.Float{Value: 93.11},
|
||||
compiledFunction(1, 0,
|
||||
|
@ -36,11 +41,12 @@ func TestBytecode(t *testing.T) {
|
|||
&objects.Map{
|
||||
Value: map[string]objects.Object{
|
||||
"a": &objects.Float{Value: -93.1},
|
||||
"b": &objects.Bool{Value: false},
|
||||
"b": objects.FalseValue,
|
||||
"c": objects.UndefinedValue,
|
||||
},
|
||||
},
|
||||
&objects.String{Value: "bar"},
|
||||
&objects.Undefined{})))
|
||||
objects.UndefinedValue)))
|
||||
|
||||
testBytecodeSerialization(t, bytecode(
|
||||
concat(
|
||||
|
|
|
@ -43,7 +43,11 @@ func FuncARB(fn func() bool) *objects.UserFunction {
|
|||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
return &objects.Bool{Value: fn()}, nil
|
||||
if fn() {
|
||||
return objects.TrueValue, nil
|
||||
}
|
||||
|
||||
return objects.FalseValue, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -340,7 +344,11 @@ func FuncAFIRB(fn func(float64, int) bool) *objects.UserFunction {
|
|||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
return &objects.Bool{Value: fn(f1, i2)}, nil
|
||||
if fn(f1, i2) {
|
||||
return objects.TrueValue, nil
|
||||
}
|
||||
|
||||
return objects.FalseValue, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -359,7 +367,11 @@ func FuncAFRB(fn func(float64) bool) *objects.UserFunction {
|
|||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
return &objects.Bool{Value: fn(f1)}, nil
|
||||
if fn(f1) {
|
||||
return objects.TrueValue, nil
|
||||
}
|
||||
|
||||
return objects.FalseValue, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -383,6 +395,31 @@ func FuncASRS(fn func(string) string) *objects.UserFunction {
|
|||
}
|
||||
}
|
||||
|
||||
// FuncASRSs transform a function of 'func(string) []string' signature into a user function object.
|
||||
func FuncASRSs(fn func(string) []string) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
res := fn(s1)
|
||||
|
||||
arr := &objects.Array{}
|
||||
for _, osArg := range res {
|
||||
arr.Value = append(arr.Value, &objects.String{Value: osArg})
|
||||
}
|
||||
|
||||
return arr, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncASRSE transform a function of 'func(string) (string, error)' signature into a user function object.
|
||||
// User function will return 'true' if underlying native function returns nil.
|
||||
func FuncASRSE(fn func(string) (string, error)) *objects.UserFunction {
|
||||
|
@ -450,6 +487,171 @@ func FuncASSRE(fn func(string, string) error) *objects.UserFunction {
|
|||
}
|
||||
}
|
||||
|
||||
// FuncASSRSs transform a function of 'func(string, string) []string' signature into a user function object.
|
||||
func FuncASSRSs(fn func(string, string) []string) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 2 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for _, res := range fn(s1, s2) {
|
||||
arr.Value = append(arr.Value, &objects.String{Value: res})
|
||||
}
|
||||
|
||||
return arr, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncASSIRSs transform a function of 'func(string, string, int) []string' signature into a user function object.
|
||||
func FuncASSIRSs(fn func(string, string, int) []string) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 3 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for _, res := range fn(s1, s2, i3) {
|
||||
arr.Value = append(arr.Value, &objects.String{Value: res})
|
||||
}
|
||||
|
||||
return arr, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncASSRI transform a function of 'func(string, string) int' signature into a user function object.
|
||||
func FuncASSRI(fn func(string, string) int) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 2 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
return &objects.Int{Value: int64(fn(s1, s2))}, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncASSRS transform a function of 'func(string, string) string' signature into a user function object.
|
||||
func FuncASSRS(fn func(string, string) string) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 2 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
return &objects.String{Value: fn(s1, s2)}, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncASSRB transform a function of 'func(string, string) bool' signature into a user function object.
|
||||
func FuncASSRB(fn func(string, string) bool) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 2 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
if fn(s1, s2) {
|
||||
return objects.TrueValue, nil
|
||||
}
|
||||
|
||||
return objects.FalseValue, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncASsSRS transform a function of 'func([]string, string) string' signature into a user function object.
|
||||
func FuncASsSRS(fn func([]string, string) string) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 2 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
var ss1 []string
|
||||
arr, ok := args[0].(*objects.Array)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
for _, a := range arr.Value {
|
||||
as, ok := objects.ToString(a)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
ss1 = append(ss1, as)
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
return &objects.String{Value: fn(ss1, s2)}, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncASI64RE transform a function of 'func(string, int64) error' signature
|
||||
// into a user function object.
|
||||
func FuncASI64RE(fn func(string, int64) error) *objects.UserFunction {
|
||||
|
@ -498,6 +700,30 @@ func FuncAIIRE(fn func(int, int) error) *objects.UserFunction {
|
|||
}
|
||||
}
|
||||
|
||||
// FuncASIRS transform a function of 'func(string, int) string' signature
|
||||
// into a user function object.
|
||||
func FuncASIRS(fn func(string, int) string) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
return &objects.String{Value: fn(s1, i2)}, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncASIIRE transform a function of 'func(string, int, int) error' signature
|
||||
// into a user function object.
|
||||
func FuncASIIRE(fn func(string, int, int) error) *objects.UserFunction {
|
||||
|
@ -595,11 +821,30 @@ func FuncAIRSsE(fn func(int) ([]string, error)) *objects.UserFunction {
|
|||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for _, osArg := range res {
|
||||
arr.Value = append(arr.Value, &objects.String{Value: osArg})
|
||||
for _, r := range res {
|
||||
arr.Value = append(arr.Value, &objects.String{Value: r})
|
||||
}
|
||||
|
||||
return arr, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncAIRS transform a function of 'func(int) string' signature
|
||||
// into a user function object.
|
||||
func FuncAIRS(fn func(int) string) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
return &objects.String{Value: fn(i1)}, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package stdlib_test
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/assert"
|
||||
|
@ -13,7 +15,7 @@ func TestFuncAIR(t *testing.T) {
|
|||
uf := stdlib.FuncAIR(func(int) {})
|
||||
ret, err := uf.Call(&objects.Int{Value: 10})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Undefined{}, ret)
|
||||
assert.Equal(t, objects.UndefinedValue, ret)
|
||||
ret, err = uf.Call()
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
@ -22,7 +24,7 @@ func TestFuncAR(t *testing.T) {
|
|||
uf := stdlib.FuncAR(func() {})
|
||||
ret, err := uf.Call()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Undefined{}, ret)
|
||||
assert.Equal(t, objects.UndefinedValue, ret)
|
||||
ret, err = uf.Call(objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
@ -115,6 +117,15 @@ func TestFuncASRS(t *testing.T) {
|
|||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASRSs(t *testing.T) {
|
||||
uf := stdlib.FuncASRSs(func(a string) []string { return []string{a} })
|
||||
ret, err := uf.Call(&objects.String{Value: "foo"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, array(&objects.String{Value: "foo"}), ret)
|
||||
ret, err = uf.Call()
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASI64RE(t *testing.T) {
|
||||
uf := stdlib.FuncASI64RE(func(a string, b int64) error { return nil })
|
||||
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.Int{Value: 5})
|
||||
|
@ -168,7 +179,24 @@ func TestFuncASRSE(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFuncASSRE(t *testing.T) {
|
||||
uf := stdlib.FuncASSRE(func(a, b string) error { return nil })
|
||||
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"})
|
||||
assert.NoError(t, err)
|
||||
uf = stdlib.FuncASSRE(func(a, b string) error { return errors.New("some error") })
|
||||
ret, err = uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
|
||||
ret, err = uf.Call(&objects.String{Value: "foo"})
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASsRS(t *testing.T) {
|
||||
uf := stdlib.FuncASsSRS(func(a []string, b string) string { return strings.Join(a, b) })
|
||||
ret, err := uf.Call(array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}), &objects.String{Value: " "})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.String{Value: "foo bar"}, ret)
|
||||
ret, err = uf.Call(&objects.String{Value: "foo"})
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncARF(t *testing.T) {
|
||||
|
@ -227,7 +255,7 @@ func TestFuncAFRB(t *testing.T) {
|
|||
})
|
||||
ret, err := uf.Call(&objects.Float{Value: 0.1})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Bool{Value: true}, ret)
|
||||
assert.Equal(t, objects.TrueValue, ret)
|
||||
ret, err = uf.Call()
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
ret, err = uf.Call(objects.TrueValue, objects.TrueValue)
|
||||
|
@ -247,6 +275,17 @@ func TestFuncAFFRF(t *testing.T) {
|
|||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASIRS(t *testing.T) {
|
||||
uf := stdlib.FuncASIRS(func(a string, b int) string { return strings.Repeat(a, b) })
|
||||
ret, err := uf.Call(&objects.String{Value: "ab"}, &objects.Int{Value: 2})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.String{Value: "abab"}, ret)
|
||||
ret, err = uf.Call()
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
ret, err = uf.Call(objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncAIFRF(t *testing.T) {
|
||||
uf := stdlib.FuncAIFRF(func(a int, b float64) float64 {
|
||||
return float64(a) + b
|
||||
|
@ -279,7 +318,7 @@ func TestFuncAFIRB(t *testing.T) {
|
|||
})
|
||||
ret, err := uf.Call(&objects.Float{Value: 10.0}, &objects.Int{Value: 20})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Bool{Value: true}, ret)
|
||||
assert.Equal(t, objects.TrueValue, ret)
|
||||
ret, err = uf.Call()
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
ret, err = uf.Call(objects.TrueValue)
|
||||
|
@ -303,6 +342,24 @@ func TestFuncAIRSsE(t *testing.T) {
|
|||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASSRSs(t *testing.T) {
|
||||
uf := stdlib.FuncASSRSs(func(a, b string) []string { return []string{a, b} })
|
||||
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}), ret)
|
||||
ret, err = uf.Call()
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASSIRSs(t *testing.T) {
|
||||
uf := stdlib.FuncASSIRSs(func(a, b string, c int) []string { return []string{a, b, strconv.Itoa(c)} })
|
||||
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}, &objects.Int{Value: 5})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}, &objects.String{Value: "5"}), ret)
|
||||
ret, err = uf.Call()
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncARB(t *testing.T) {
|
||||
uf := stdlib.FuncARB(func() bool { return true })
|
||||
ret, err := uf.Call()
|
||||
|
@ -355,6 +412,42 @@ func TestFuncAYRIE(t *testing.T) {
|
|||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASSRI(t *testing.T) {
|
||||
uf := stdlib.FuncASSRI(func(a, b string) int { return len(a) + len(b) })
|
||||
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Int{Value: 6}, ret)
|
||||
ret, err = uf.Call(&objects.String{Value: "foo"})
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASSRS(t *testing.T) {
|
||||
uf := stdlib.FuncASSRS(func(a, b string) string { return a + b })
|
||||
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.String{Value: "foobar"}, ret)
|
||||
ret, err = uf.Call(&objects.String{Value: "foo"})
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASSRB(t *testing.T) {
|
||||
uf := stdlib.FuncASSRB(func(a, b string) bool { return len(a) > len(b) })
|
||||
ret, err := uf.Call(&objects.String{Value: "123"}, &objects.String{Value: "12"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, objects.TrueValue, ret)
|
||||
ret, err = uf.Call(&objects.String{Value: "foo"})
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncAIRS(t *testing.T) {
|
||||
uf := stdlib.FuncAIRS(func(a int) string { return strconv.Itoa(a) })
|
||||
ret, err := uf.Call(&objects.Int{Value: 55})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.String{Value: "55"}, ret)
|
||||
ret, err = uf.Call()
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func array(elements ...objects.Object) *objects.Array {
|
||||
return &objects.Array{Value: elements}
|
||||
}
|
||||
|
|
|
@ -67,14 +67,8 @@ var mathModule = map[string]objects.Object{
|
|||
"sqrt": FuncAFRF(math.Sqrt),
|
||||
"tan": FuncAFRF(math.Tan),
|
||||
"tanh": FuncAFRF(math.Tanh),
|
||||
"runct": FuncAFRF(math.Trunc),
|
||||
"trunc": FuncAFRF(math.Trunc),
|
||||
"y0": FuncAFRF(math.Y0),
|
||||
"y1": FuncAFRF(math.Y1),
|
||||
"yn": FuncAIFRF(math.Yn),
|
||||
// TODO: functions that have multiple returns
|
||||
// Should these be tuple assignment? Or Map return?
|
||||
//"frexp": nil,
|
||||
//"lgamma": nil,
|
||||
//"modf": nil,
|
||||
//"sincos": nil,
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package stdlib
|
|||
import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
@ -36,82 +37,110 @@ var osModule = map[string]objects.Object{
|
|||
"seek_set": &objects.Int{Value: int64(io.SeekStart)},
|
||||
"seek_cur": &objects.Int{Value: int64(io.SeekCurrent)},
|
||||
"seek_end": &objects.Int{Value: int64(io.SeekEnd)},
|
||||
// args() => array(string)
|
||||
"args": &objects.UserFunction{Value: osArgs},
|
||||
// chdir(dir string) => error
|
||||
"chdir": FuncASRE(os.Chdir),
|
||||
// chmod(name string, mode int) => error
|
||||
"chmod": osFuncASFmRE(os.Chmod),
|
||||
// chown(name string, uid int, gid int) => error
|
||||
"chown": FuncASIIRE(os.Chown),
|
||||
// clearenv()
|
||||
"clearenv": FuncAR(os.Clearenv),
|
||||
// environ() => array(string)
|
||||
"environ": FuncARSs(os.Environ),
|
||||
// exit(code int)
|
||||
"exit": FuncAIR(os.Exit),
|
||||
// expand_env(s string) => string
|
||||
"expand_env": FuncASRS(os.ExpandEnv),
|
||||
// getegid() => int
|
||||
"getegid": FuncARI(os.Getegid),
|
||||
// getenv(s string) => string
|
||||
"getenv": FuncASRS(os.Getenv),
|
||||
// geteuid() => int
|
||||
"geteuid": FuncARI(os.Geteuid),
|
||||
// getgid() => int
|
||||
"getgid": FuncARI(os.Getgid),
|
||||
// getgroups() => array(string)/error
|
||||
"getgroups": FuncARIsE(os.Getgroups),
|
||||
// getpagesize() => int
|
||||
"getpagesize": FuncARI(os.Getpagesize),
|
||||
// getpid() => int
|
||||
"getpid": FuncARI(os.Getpid),
|
||||
// getppid() => int
|
||||
"getppid": FuncARI(os.Getppid),
|
||||
// getuid() => int
|
||||
"getuid": FuncARI(os.Getuid),
|
||||
// getwd() => string/error
|
||||
"getwd": FuncARSE(os.Getwd),
|
||||
// hostname() => string/error
|
||||
"hostname": FuncARSE(os.Hostname),
|
||||
// lchown(name string, uid int, gid int) => error
|
||||
"lchown": FuncASIIRE(os.Lchown),
|
||||
// link(oldname string, newname string) => error
|
||||
"link": FuncASSRE(os.Link),
|
||||
// lookup_env(key string) => string/false
|
||||
"lookup_env": &objects.UserFunction{Value: osLookupEnv},
|
||||
// mkdir(name string, perm int) => error
|
||||
"mkdir": osFuncASFmRE(os.Mkdir),
|
||||
// mkdir_all(name string, perm int) => error
|
||||
"mkdir_all": osFuncASFmRE(os.MkdirAll),
|
||||
// readlink(name string) => string/error
|
||||
"readlink": FuncASRSE(os.Readlink),
|
||||
// remove(name string) => error
|
||||
"remove": FuncASRE(os.Remove),
|
||||
// remove_all(name string) => error
|
||||
"remove_all": FuncASRE(os.RemoveAll),
|
||||
// rename(oldpath string, newpath string) => error
|
||||
"rename": FuncASSRE(os.Rename),
|
||||
// setenv(key string, value string) => error
|
||||
"setenv": FuncASSRE(os.Setenv),
|
||||
// symlink(oldname string newname string) => error
|
||||
"symlink": FuncASSRE(os.Symlink),
|
||||
// temp_dir() => string
|
||||
"temp_dir": FuncARS(os.TempDir),
|
||||
// truncate(name string, size int) => error
|
||||
"truncate": FuncASI64RE(os.Truncate),
|
||||
// unsetenv(key string) => error
|
||||
"unsetenv": FuncASRE(os.Unsetenv),
|
||||
// create(name string) => imap(file)/error
|
||||
"create": &objects.UserFunction{Value: osCreate},
|
||||
// open(name string) => imap(file)/error
|
||||
"open": &objects.UserFunction{Value: osOpen},
|
||||
// open_file(name string, flag int, perm int) => imap(file)/error
|
||||
"open_file": &objects.UserFunction{Value: osOpenFile},
|
||||
// find_process(pid int) => imap(process)/error
|
||||
"find_process": &objects.UserFunction{Value: osFindProcess},
|
||||
// start_process(name string, argv array(string), dir string, env array(string)) => imap(process)/error
|
||||
"start_process": &objects.UserFunction{Value: osStartProcess},
|
||||
"args": &objects.UserFunction{Value: osArgs}, // args() => array(string)
|
||||
"chdir": FuncASRE(os.Chdir), // chdir(dir string) => error
|
||||
"chmod": osFuncASFmRE(os.Chmod), // chmod(name string, mode int) => error
|
||||
"chown": FuncASIIRE(os.Chown), // chown(name string, uid int, gid int) => error
|
||||
"clearenv": FuncAR(os.Clearenv), // clearenv()
|
||||
"environ": FuncARSs(os.Environ), // environ() => array(string)
|
||||
"exit": FuncAIR(os.Exit), // exit(code int)
|
||||
"expand_env": FuncASRS(os.ExpandEnv), // expand_env(s string) => string
|
||||
"getegid": FuncARI(os.Getegid), // getegid() => int
|
||||
"getenv": FuncASRS(os.Getenv), // getenv(s string) => string
|
||||
"geteuid": FuncARI(os.Geteuid), // geteuid() => int
|
||||
"getgid": FuncARI(os.Getgid), // getgid() => int
|
||||
"getgroups": FuncARIsE(os.Getgroups), // getgroups() => array(string)/error
|
||||
"getpagesize": FuncARI(os.Getpagesize), // getpagesize() => int
|
||||
"getpid": FuncARI(os.Getpid), // getpid() => int
|
||||
"getppid": FuncARI(os.Getppid), // getppid() => int
|
||||
"getuid": FuncARI(os.Getuid), // getuid() => int
|
||||
"getwd": FuncARSE(os.Getwd), // getwd() => string/error
|
||||
"hostname": FuncARSE(os.Hostname), // hostname() => string/error
|
||||
"lchown": FuncASIIRE(os.Lchown), // lchown(name string, uid int, gid int) => error
|
||||
"link": FuncASSRE(os.Link), // link(oldname string, newname string) => error
|
||||
"lookup_env": &objects.UserFunction{Value: osLookupEnv}, // lookup_env(key string) => string/false
|
||||
"mkdir": osFuncASFmRE(os.Mkdir), // mkdir(name string, perm int) => error
|
||||
"mkdir_all": osFuncASFmRE(os.MkdirAll), // mkdir_all(name string, perm int) => error
|
||||
"readlink": FuncASRSE(os.Readlink), // readlink(name string) => string/error
|
||||
"remove": FuncASRE(os.Remove), // remove(name string) => error
|
||||
"remove_all": FuncASRE(os.RemoveAll), // remove_all(name string) => error
|
||||
"rename": FuncASSRE(os.Rename), // rename(oldpath string, newpath string) => error
|
||||
"setenv": FuncASSRE(os.Setenv), // setenv(key string, value string) => error
|
||||
"symlink": FuncASSRE(os.Symlink), // symlink(oldname string newname string) => error
|
||||
"temp_dir": FuncARS(os.TempDir), // temp_dir() => string
|
||||
"truncate": FuncASI64RE(os.Truncate), // truncate(name string, size int) => error
|
||||
"unsetenv": FuncASRE(os.Unsetenv), // unsetenv(key string) => error
|
||||
"create": &objects.UserFunction{Value: osCreate}, // create(name string) => imap(file)/error
|
||||
"open": &objects.UserFunction{Value: osOpen}, // open(name string) => imap(file)/error
|
||||
"open_file": &objects.UserFunction{Value: osOpenFile}, // open_file(name string, flag int, perm int) => imap(file)/error
|
||||
"find_process": &objects.UserFunction{Value: osFindProcess}, // find_process(pid int) => imap(process)/error
|
||||
"start_process": &objects.UserFunction{Value: osStartProcess}, // start_process(name string, argv array(string), dir string, env array(string)) => imap(process)/error
|
||||
"exec_look_path": FuncASRSE(exec.LookPath), // exec_look_path(file) => string/error
|
||||
"exec": &objects.UserFunction{Value: osExec}, // exec(name, args...) => command
|
||||
}
|
||||
|
||||
func osCreate(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
res, err := os.Create(s1)
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return makeOSFile(res), nil
|
||||
}
|
||||
|
||||
func osOpen(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
res, err := os.Open(s1)
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return makeOSFile(res), nil
|
||||
}
|
||||
|
||||
func osOpenFile(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 3 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
res, err := os.OpenFile(s1, i2, os.FileMode(i3))
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return makeOSFile(res), nil
|
||||
}
|
||||
|
||||
func osArgs(args ...objects.Object) (objects.Object, error) {
|
||||
|
@ -148,19 +177,6 @@ func osFuncASFmRE(fn func(string, os.FileMode) error) *objects.UserFunction {
|
|||
}
|
||||
}
|
||||
|
||||
func osExecutable(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 0 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
res, err := os.Executable()
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return &objects.String{Value: res}, nil
|
||||
}
|
||||
|
||||
func osLookupEnv(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
|
@ -178,3 +194,99 @@ func osLookupEnv(args ...objects.Object) (objects.Object, error) {
|
|||
|
||||
return &objects.String{Value: res}, nil
|
||||
}
|
||||
|
||||
func osExec(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) == 0 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
name, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
var execArgs []string
|
||||
for _, arg := range args[1:] {
|
||||
execArg, ok := objects.ToString(arg)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
execArgs = append(execArgs, execArg)
|
||||
}
|
||||
|
||||
return makeOSExecCommand(exec.Command(name, execArgs...)), nil
|
||||
}
|
||||
|
||||
func osFindProcess(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
proc, err := os.FindProcess(i1)
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return makeOSProcess(proc), nil
|
||||
}
|
||||
|
||||
func osStartProcess(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 4 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
name, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
argv, err := stringArray(args[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dir, ok := objects.ToString(args[2])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
env, err := stringArray(args[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proc, err := os.StartProcess(name, argv, &os.ProcAttr{
|
||||
Dir: dir,
|
||||
Env: env,
|
||||
})
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return makeOSProcess(proc), nil
|
||||
}
|
||||
|
||||
func stringArray(o objects.Object) ([]string, error) {
|
||||
arr, ok := o.(*objects.Array)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
var sarr []string
|
||||
for _, elem := range arr.Value {
|
||||
str, ok := elem.(*objects.String)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
sarr = append(sarr, str.Value)
|
||||
}
|
||||
|
||||
return sarr, nil
|
||||
}
|
||||
|
|
|
@ -6,14 +6,7 @@ import (
|
|||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
var execModule = map[string]objects.Object{
|
||||
// look_path(file string) => string/error
|
||||
"look_path": FuncASRSE(exec.LookPath),
|
||||
// command(name string, args array(string)) => imap(cmd)
|
||||
"command": &objects.UserFunction{Value: execCommand},
|
||||
}
|
||||
|
||||
func execCmdImmutableMap(cmd *exec.Cmd) *objects.ImmutableMap {
|
||||
func makeOSExecCommand(cmd *exec.Cmd) *objects.ImmutableMap {
|
||||
return &objects.ImmutableMap{
|
||||
Value: map[string]objects.Object{
|
||||
// combined_output() => bytes/error
|
||||
|
@ -84,33 +77,9 @@ func execCmdImmutableMap(cmd *exec.Cmd) *objects.ImmutableMap {
|
|||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
return osProcessImmutableMap(cmd.Process), nil
|
||||
return makeOSProcess(cmd.Process), nil
|
||||
},
|
||||
},
|
||||
// TODO: implement pipes
|
||||
//"stderr_pipe": nil,
|
||||
//"stdin_pipe": nil,
|
||||
//"stdout_pipe": nil,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func execCommand(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 2 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
name, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
arg, err := stringArray(args[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := exec.Command(name, arg...)
|
||||
|
||||
return execCmdImmutableMap(res), nil
|
||||
}
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func osFileImmutableMap(file *os.File) *objects.ImmutableMap {
|
||||
func makeOSFile(file *os.File) *objects.ImmutableMap {
|
||||
return &objects.ImmutableMap{
|
||||
Value: map[string]objects.Object{
|
||||
// chdir() => true/error
|
||||
|
@ -66,79 +66,6 @@ func osFileImmutableMap(file *os.File) *objects.ImmutableMap {
|
|||
return &objects.Int{Value: res}, nil
|
||||
},
|
||||
},
|
||||
// TODO: implement more functions
|
||||
//"fd": nil,
|
||||
//"read_at": nil,
|
||||
//"readdir": nil,
|
||||
//"set_deadline": nil,
|
||||
//"set_read_deadline": nil,
|
||||
//"set_write_deadline": nil,
|
||||
//"stat": nil,
|
||||
//"write_at": nil,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func osCreate(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
res, err := os.Create(s1)
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return osFileImmutableMap(res), nil
|
||||
}
|
||||
|
||||
func osOpen(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
res, err := os.Open(s1)
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return osFileImmutableMap(res), nil
|
||||
}
|
||||
|
||||
func osOpenFile(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 3 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
res, err := os.OpenFile(s1, i2, os.FileMode(i3))
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return osFileImmutableMap(res), nil
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func osProcessStateImmutableMap(state *os.ProcessState) *objects.ImmutableMap {
|
||||
func makeOSProcessState(state *os.ProcessState) *objects.ImmutableMap {
|
||||
return &objects.ImmutableMap{
|
||||
Value: map[string]objects.Object{
|
||||
"exited": FuncARB(state.Exited),
|
||||
|
@ -18,7 +18,7 @@ func osProcessStateImmutableMap(state *os.ProcessState) *objects.ImmutableMap {
|
|||
}
|
||||
}
|
||||
|
||||
func osProcessImmutableMap(proc *os.Process) *objects.ImmutableMap {
|
||||
func makeOSProcess(proc *os.Process) *objects.ImmutableMap {
|
||||
return &objects.ImmutableMap{
|
||||
Value: map[string]objects.Object{
|
||||
"kill": FuncARE(proc.Kill),
|
||||
|
@ -48,82 +48,9 @@ func osProcessImmutableMap(proc *os.Process) *objects.ImmutableMap {
|
|||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return osProcessStateImmutableMap(state), nil
|
||||
return makeOSProcessState(state), nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func osFindProcess(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
proc, err := os.FindProcess(i1)
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return osProcessImmutableMap(proc), nil
|
||||
}
|
||||
|
||||
func osStartProcess(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 4 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
name, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
argv, err := stringArray(args[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dir, ok := objects.ToString(args[2])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
env, err := stringArray(args[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proc, err := os.StartProcess(name, argv, &os.ProcAttr{
|
||||
Dir: dir,
|
||||
Env: env,
|
||||
})
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return osProcessImmutableMap(proc), nil
|
||||
}
|
||||
|
||||
func stringArray(o objects.Object) ([]string, error) {
|
||||
arr, ok := o.(*objects.Array)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
var sarr []string
|
||||
for _, elem := range arr.Value {
|
||||
str, ok := elem.(*objects.String)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
sarr = append(sarr, str.Value)
|
||||
}
|
||||
|
||||
return sarr, nil
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ import "github.com/d5/tengo/objects"
|
|||
|
||||
// Modules contain the standard modules.
|
||||
var Modules = map[string]*objects.ImmutableMap{
|
||||
"math": {Value: mathModule},
|
||||
"os": {Value: osModule},
|
||||
"exec": {Value: execModule},
|
||||
"math": {Value: mathModule},
|
||||
"os": {Value: osModule},
|
||||
"text": {Value: textModule},
|
||||
"times": {Value: timesModule},
|
||||
}
|
||||
|
|
128
compiler/stdlib/stdlib_test.go
Normal file
128
compiler/stdlib/stdlib_test.go
Normal file
|
@ -0,0 +1,128 @@
|
|||
package stdlib_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/d5/tengo/assert"
|
||||
"github.com/d5/tengo/compiler/stdlib"
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
type ARR = []interface{}
|
||||
type MAP = map[string]interface{}
|
||||
type IARR []interface{}
|
||||
type IMAP map[string]interface{}
|
||||
|
||||
type callres struct {
|
||||
t *testing.T
|
||||
o objects.Object
|
||||
e error
|
||||
}
|
||||
|
||||
func (c callres) call(funcName string, args ...interface{}) callres {
|
||||
if c.e != nil {
|
||||
return c
|
||||
}
|
||||
|
||||
imap, ok := c.o.(*objects.ImmutableMap)
|
||||
if !ok {
|
||||
return c
|
||||
}
|
||||
|
||||
m, ok := imap.Value[funcName]
|
||||
if !ok {
|
||||
return callres{t: c.t, e: fmt.Errorf("function not found: %s", funcName)}
|
||||
}
|
||||
|
||||
f, ok := m.(*objects.UserFunction)
|
||||
if !ok {
|
||||
return callres{t: c.t, e: fmt.Errorf("non-callable: %s", funcName)}
|
||||
}
|
||||
|
||||
var oargs []objects.Object
|
||||
for _, v := range args {
|
||||
oargs = append(oargs, object(v))
|
||||
}
|
||||
|
||||
res, err := f.Value(oargs...)
|
||||
|
||||
return callres{t: c.t, o: res, e: err}
|
||||
}
|
||||
|
||||
func (c callres) expect(expected interface{}, msgAndArgs ...interface{}) bool {
|
||||
return assert.NoError(c.t, c.e, msgAndArgs...) &&
|
||||
assert.Equal(c.t, object(expected), c.o, msgAndArgs...)
|
||||
}
|
||||
|
||||
func (c callres) expectError() bool {
|
||||
return assert.Error(c.t, c.e)
|
||||
}
|
||||
|
||||
func module(t *testing.T, moduleName string) callres {
|
||||
mod, ok := stdlib.Modules[moduleName]
|
||||
if !ok {
|
||||
return callres{t: t, e: fmt.Errorf("module not found: %s", moduleName)}
|
||||
}
|
||||
|
||||
return callres{t: t, o: mod}
|
||||
}
|
||||
|
||||
func object(v interface{}) objects.Object {
|
||||
switch v := v.(type) {
|
||||
case objects.Object:
|
||||
return v
|
||||
case string:
|
||||
return &objects.String{Value: v}
|
||||
case int64:
|
||||
return &objects.Int{Value: v}
|
||||
case int: // for convenience
|
||||
return &objects.Int{Value: int64(v)}
|
||||
case bool:
|
||||
if v {
|
||||
return objects.TrueValue
|
||||
}
|
||||
return objects.FalseValue
|
||||
case rune:
|
||||
return &objects.Char{Value: v}
|
||||
case byte: // for convenience
|
||||
return &objects.Char{Value: rune(v)}
|
||||
case float64:
|
||||
return &objects.Float{Value: v}
|
||||
case []byte:
|
||||
return &objects.Bytes{Value: v}
|
||||
case MAP:
|
||||
objs := make(map[string]objects.Object)
|
||||
for k, v := range v {
|
||||
objs[k] = object(v)
|
||||
}
|
||||
|
||||
return &objects.Map{Value: objs}
|
||||
case ARR:
|
||||
var objs []objects.Object
|
||||
for _, e := range v {
|
||||
objs = append(objs, object(e))
|
||||
}
|
||||
|
||||
return &objects.Array{Value: objs}
|
||||
case IMAP:
|
||||
objs := make(map[string]objects.Object)
|
||||
for k, v := range v {
|
||||
objs[k] = object(v)
|
||||
}
|
||||
|
||||
return &objects.ImmutableMap{Value: objs}
|
||||
case IARR:
|
||||
var objs []objects.Object
|
||||
for _, e := range v {
|
||||
objs = append(objs, object(e))
|
||||
}
|
||||
|
||||
return &objects.ImmutableArray{Value: objs}
|
||||
case time.Time:
|
||||
return &objects.Time{Value: v}
|
||||
}
|
||||
|
||||
panic(fmt.Errorf("unknown type: %T", v))
|
||||
}
|
469
compiler/stdlib/text.go
Normal file
469
compiler/stdlib/text.go
Normal file
|
@ -0,0 +1,469 @@
|
|||
package stdlib
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
var textModule = map[string]objects.Object{
|
||||
"re_match": &objects.UserFunction{Value: textREMatch}, // re_match(pattern, text) => bool/error
|
||||
"re_find": &objects.UserFunction{Value: textREFind}, // re_find(pattern, text, count) => [[{text:,begin:,end:}]]/undefined
|
||||
"re_replace": &objects.UserFunction{Value: textREReplace}, // re_replace(pattern, text, repl) => string/error
|
||||
"re_split": &objects.UserFunction{Value: textRESplit}, // re_split(pattern, text, count) => [string]/error
|
||||
"re_compile": &objects.UserFunction{Value: textRECompile}, // re_compile(pattern) => Regexp/error
|
||||
"compare": FuncASSRI(strings.Compare), // compare(a, b) => int
|
||||
"contains": FuncASSRB(strings.Contains), // contains(s, substr) => bool
|
||||
"contains_any": FuncASSRB(strings.ContainsAny), // contains_any(s, chars) => bool
|
||||
"count": FuncASSRI(strings.Count), // count(s, substr) => int
|
||||
"equal_fold": FuncASSRB(strings.EqualFold), // "equal_fold(s, t) => bool
|
||||
"fields": FuncASRSs(strings.Fields), // fields(s) => [string]
|
||||
"has_prefix": FuncASSRB(strings.HasPrefix), // has_prefix(s, prefix) => bool
|
||||
"has_suffix": FuncASSRB(strings.HasSuffix), // has_suffix(s, suffix) => bool
|
||||
"index": FuncASSRI(strings.Index), // index(s, substr) => int
|
||||
"index_any": FuncASSRI(strings.IndexAny), // index_any(s, chars) => int
|
||||
"join": FuncASsSRS(strings.Join), // join(arr, sep) => string
|
||||
"last_index": FuncASSRI(strings.LastIndex), // last_index(s, substr) => int
|
||||
"last_index_any": FuncASSRI(strings.LastIndexAny), // last_index_any(s, chars) => int
|
||||
"repeat": FuncASIRS(strings.Repeat), // repeat(s, count) => string
|
||||
"replace": &objects.UserFunction{Value: textReplace}, // replace(s, old, new, n) => string
|
||||
"split": FuncASSRSs(strings.Split), // split(s, sep) => [string]
|
||||
"split_after": FuncASSRSs(strings.SplitAfter), // split_after(s, sep) => [string]
|
||||
"split_after_n": FuncASSIRSs(strings.SplitAfterN), // split_after_n(s, sep, n) => [string]
|
||||
"split_n": FuncASSIRSs(strings.SplitN), // split_n(s, sep, n) => [string]
|
||||
"title": FuncASRS(strings.Title), // title(s) => string
|
||||
"to_lower": FuncASRS(strings.ToLower), // to_lower(s) => string
|
||||
"to_title": FuncASRS(strings.ToTitle), // to_title(s) => string
|
||||
"to_upper": FuncASRS(strings.ToUpper), // to_upper(s) => string
|
||||
"trim_left": FuncASSRS(strings.TrimLeft), // trim_left(s, cutset) => string
|
||||
"trim_prefix": FuncASSRS(strings.TrimPrefix), // trim_prefix(s, prefix) => string
|
||||
"trim_right": FuncASSRS(strings.TrimRight), // trim_right(s, cutset) => string
|
||||
"trim_space": FuncASRS(strings.TrimSpace), // trim_space(s) => string
|
||||
"trim_suffix": FuncASSRS(strings.TrimSuffix), // trim_suffix(s, suffix) => string
|
||||
"atoi": FuncASRIE(strconv.Atoi), // atoi(str) => int/error
|
||||
"format_bool": &objects.UserFunction{Value: textFormatBool}, // format_bool(b) => string
|
||||
"format_float": &objects.UserFunction{Value: textFormatFloat}, // format_float(f, fmt, prec, bits) => string
|
||||
"format_int": &objects.UserFunction{Value: textFormatInt}, // format_int(i, base) => string
|
||||
"itoa": FuncAIRS(strconv.Itoa), // itoa(i) => string
|
||||
"parse_bool": &objects.UserFunction{Value: textParseBool}, // parse_bool(str) => bool/error
|
||||
"parse_float": &objects.UserFunction{Value: textParseFloat}, // parse_float(str, bits) => float/error
|
||||
"parse_int": &objects.UserFunction{Value: textParseInt}, // parse_int(str, base, bits) => int/error
|
||||
"quote": FuncASRS(strconv.Quote), // quote(str) => string
|
||||
"unquote": FuncASRSE(strconv.Unquote), // unquote(str) => string/error
|
||||
}
|
||||
|
||||
func textREMatch(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
matched, err := regexp.MatchString(s1, s2)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if matched {
|
||||
ret = objects.TrueValue
|
||||
} else {
|
||||
ret = objects.FalseValue
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textREFind(args ...objects.Object) (ret objects.Object, err error) {
|
||||
numArgs := len(args)
|
||||
if numArgs != 2 && numArgs != 3 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(s1)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
if numArgs < 3 {
|
||||
m := re.FindStringSubmatchIndex(s2)
|
||||
if m == nil {
|
||||
ret = objects.UndefinedValue
|
||||
return
|
||||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for i := 0; i < len(m); i += 2 {
|
||||
arr.Value = append(arr.Value, &objects.ImmutableMap{Value: map[string]objects.Object{
|
||||
"text": &objects.String{Value: s2[m[i]:m[i+1]]},
|
||||
"begin": &objects.Int{Value: int64(m[i])},
|
||||
"end": &objects.Int{Value: int64(m[i+1])},
|
||||
}})
|
||||
}
|
||||
|
||||
ret = &objects.Array{Value: []objects.Object{arr}}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
m := re.FindAllStringSubmatchIndex(s2, i3)
|
||||
if m == nil {
|
||||
ret = objects.UndefinedValue
|
||||
return
|
||||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for _, m := range m {
|
||||
subMatch := &objects.Array{}
|
||||
for i := 0; i < len(m); i += 2 {
|
||||
subMatch.Value = append(subMatch.Value, &objects.ImmutableMap{Value: map[string]objects.Object{
|
||||
"text": &objects.String{Value: s2[m[i]:m[i+1]]},
|
||||
"begin": &objects.Int{Value: int64(m[i])},
|
||||
"end": &objects.Int{Value: int64(m[i+1])},
|
||||
}})
|
||||
}
|
||||
|
||||
arr.Value = append(arr.Value, subMatch)
|
||||
}
|
||||
|
||||
ret = arr
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textREReplace(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 3 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s3, ok := objects.ToString(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(s1)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
} else {
|
||||
ret = &objects.String{Value: re.ReplaceAllString(s2, s3)}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textRESplit(args ...objects.Object) (ret objects.Object, err error) {
|
||||
numArgs := len(args)
|
||||
if numArgs != 2 && numArgs != 3 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
var i3 = -1
|
||||
if numArgs > 2 {
|
||||
i3, ok = objects.ToInt(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(s1)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for _, s := range re.Split(s2, i3) {
|
||||
arr.Value = append(arr.Value, &objects.String{Value: s})
|
||||
}
|
||||
|
||||
ret = arr
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textRECompile(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(s1)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
} else {
|
||||
ret = makeTextRegexp(re)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textReplace(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 4 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s3, ok := objects.ToString(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i4, ok := objects.ToInt(args[3])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.String{Value: strings.Replace(s1, s2, s3, i4)}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textFormatBool(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
b1, ok := args[0].(*objects.Bool)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
if b1 == objects.TrueValue {
|
||||
ret = &objects.String{Value: "true"}
|
||||
} else {
|
||||
ret = &objects.String{Value: "false"}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textFormatFloat(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 4 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
f1, ok := args[0].(*objects.Float)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i4, ok := objects.ToInt(args[3])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.String{Value: strconv.FormatFloat(f1.Value, s2[0], i3, i4)}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textFormatInt(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
i1, ok := args[0].(*objects.Int)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.String{Value: strconv.FormatInt(i1.Value, i2)}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textParseBool(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := args[0].(*objects.String)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
parsed, err := strconv.ParseBool(s1.Value)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if parsed {
|
||||
ret = objects.TrueValue
|
||||
} else {
|
||||
ret = objects.FalseValue
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textParseFloat(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := args[0].(*objects.String)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
parsed, err := strconv.ParseFloat(s1.Value, i2)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Float{Value: parsed}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textParseInt(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 3 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := args[0].(*objects.String)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
parsed, err := strconv.ParseInt(s1.Value, i2, i3)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: parsed}
|
||||
|
||||
return
|
||||
}
|
167
compiler/stdlib/text_regexp.go
Normal file
167
compiler/stdlib/text_regexp.go
Normal file
|
@ -0,0 +1,167 @@
|
|||
package stdlib
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap {
|
||||
return &objects.ImmutableMap{
|
||||
Value: map[string]objects.Object{
|
||||
// match(text) => bool
|
||||
"match": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
if re.MatchString(s1) {
|
||||
ret = objects.TrueValue
|
||||
} else {
|
||||
ret = objects.FalseValue
|
||||
}
|
||||
|
||||
return
|
||||
},
|
||||
},
|
||||
|
||||
// find(text) => array(array({text:,begin:,end:}))/undefined
|
||||
// find(text, maxCount) => array(array({text:,begin:,end:}))/undefined
|
||||
"find": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
numArgs := len(args)
|
||||
if numArgs != 1 && numArgs != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
if numArgs == 1 {
|
||||
m := re.FindStringSubmatchIndex(s1)
|
||||
if m == nil {
|
||||
ret = objects.UndefinedValue
|
||||
return
|
||||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for i := 0; i < len(m); i += 2 {
|
||||
arr.Value = append(arr.Value, &objects.ImmutableMap{Value: map[string]objects.Object{
|
||||
"text": &objects.String{Value: s1[m[i]:m[i+1]]},
|
||||
"begin": &objects.Int{Value: int64(m[i])},
|
||||
"end": &objects.Int{Value: int64(m[i+1])},
|
||||
}})
|
||||
}
|
||||
|
||||
ret = &objects.Array{Value: []objects.Object{arr}}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
m := re.FindAllStringSubmatchIndex(s1, i2)
|
||||
if m == nil {
|
||||
ret = objects.UndefinedValue
|
||||
return
|
||||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for _, m := range m {
|
||||
subMatch := &objects.Array{}
|
||||
for i := 0; i < len(m); i += 2 {
|
||||
subMatch.Value = append(subMatch.Value, &objects.ImmutableMap{Value: map[string]objects.Object{
|
||||
"text": &objects.String{Value: s1[m[i]:m[i+1]]},
|
||||
"begin": &objects.Int{Value: int64(m[i])},
|
||||
"end": &objects.Int{Value: int64(m[i+1])},
|
||||
}})
|
||||
}
|
||||
|
||||
arr.Value = append(arr.Value, subMatch)
|
||||
}
|
||||
|
||||
ret = arr
|
||||
|
||||
return
|
||||
},
|
||||
},
|
||||
|
||||
// replace(src, repl) => string
|
||||
"replace": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.String{Value: re.ReplaceAllString(s1, s2)}
|
||||
|
||||
return
|
||||
},
|
||||
},
|
||||
|
||||
// split(text) => array(string)
|
||||
// split(text, maxCount) => array(string)
|
||||
"split": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
numArgs := len(args)
|
||||
if numArgs != 1 && numArgs != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
var i2 = -1
|
||||
if numArgs > 1 {
|
||||
i2, ok = objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for _, s := range re.Split(s1, i2) {
|
||||
arr.Value = append(arr.Value, &objects.String{Value: s})
|
||||
}
|
||||
|
||||
ret = arr
|
||||
|
||||
return
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
200
compiler/stdlib/text_test.go
Normal file
200
compiler/stdlib/text_test.go
Normal file
|
@ -0,0 +1,200 @@
|
|||
package stdlib_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func TestTextRE(t *testing.T) {
|
||||
// re_match(pattern, text)
|
||||
for _, d := range []struct {
|
||||
pattern string
|
||||
text string
|
||||
expected interface{}
|
||||
}{
|
||||
{"abc", "", false},
|
||||
{"abc", "abc", true},
|
||||
{"a", "abc", true},
|
||||
{"b", "abc", true},
|
||||
{"^a", "abc", true},
|
||||
{"^b", "abc", false},
|
||||
} {
|
||||
module(t, "text").call("re_match", d.pattern, d.text).expect(d.expected, "pattern: %q, src: %q", d.pattern, d.text)
|
||||
module(t, "text").call("re_compile", d.pattern).call("match", d.text).expect(d.expected, "patter: %q, src: %q", d.pattern, d.text)
|
||||
}
|
||||
|
||||
// re_find(pattern, text)
|
||||
for _, d := range []struct {
|
||||
pattern string
|
||||
text string
|
||||
expected interface{}
|
||||
}{
|
||||
{"a(b)", "", objects.UndefinedValue},
|
||||
{"a(b)", "ab", ARR{
|
||||
ARR{
|
||||
IMAP{"text": "ab", "begin": 0, "end": 2},
|
||||
IMAP{"text": "b", "begin": 1, "end": 2},
|
||||
},
|
||||
}},
|
||||
{"a(bc)d", "abcdefgabcd", ARR{
|
||||
ARR{
|
||||
IMAP{"text": "abcd", "begin": 0, "end": 4},
|
||||
IMAP{"text": "bc", "begin": 1, "end": 3},
|
||||
},
|
||||
}},
|
||||
{"(a)b(c)d", "abcdefgabcd", ARR{
|
||||
ARR{
|
||||
IMAP{"text": "abcd", "begin": 0, "end": 4},
|
||||
IMAP{"text": "a", "begin": 0, "end": 1},
|
||||
IMAP{"text": "c", "begin": 2, "end": 3},
|
||||
},
|
||||
}},
|
||||
} {
|
||||
module(t, "text").call("re_find", d.pattern, d.text).expect(d.expected, "pattern: %q, text: %q", d.pattern, d.text)
|
||||
module(t, "text").call("re_compile", d.pattern).call("find", d.text).expect(d.expected, "pattern: %q, text: %q", d.pattern, d.text)
|
||||
}
|
||||
|
||||
// re_find(pattern, text, count))
|
||||
for _, d := range []struct {
|
||||
pattern string
|
||||
text string
|
||||
count int
|
||||
expected interface{}
|
||||
}{
|
||||
{"a(b)", "", -1, objects.UndefinedValue},
|
||||
{"a(b)", "ab", -1, ARR{
|
||||
ARR{
|
||||
IMAP{"text": "ab", "begin": 0, "end": 2},
|
||||
IMAP{"text": "b", "begin": 1, "end": 2},
|
||||
},
|
||||
}},
|
||||
{"a(bc)d", "abcdefgabcd", -1, ARR{
|
||||
ARR{
|
||||
IMAP{"text": "abcd", "begin": 0, "end": 4},
|
||||
IMAP{"text": "bc", "begin": 1, "end": 3},
|
||||
},
|
||||
ARR{
|
||||
IMAP{"text": "abcd", "begin": 7, "end": 11},
|
||||
IMAP{"text": "bc", "begin": 8, "end": 10},
|
||||
},
|
||||
}},
|
||||
{"(a)b(c)d", "abcdefgabcd", -1, ARR{
|
||||
ARR{
|
||||
IMAP{"text": "abcd", "begin": 0, "end": 4},
|
||||
IMAP{"text": "a", "begin": 0, "end": 1},
|
||||
IMAP{"text": "c", "begin": 2, "end": 3},
|
||||
},
|
||||
ARR{
|
||||
IMAP{"text": "abcd", "begin": 7, "end": 11},
|
||||
IMAP{"text": "a", "begin": 7, "end": 8},
|
||||
IMAP{"text": "c", "begin": 9, "end": 10},
|
||||
},
|
||||
}},
|
||||
{"(a)b(c)d", "abcdefgabcd", 0, objects.UndefinedValue},
|
||||
{"(a)b(c)d", "abcdefgabcd", 1, ARR{
|
||||
ARR{
|
||||
IMAP{"text": "abcd", "begin": 0, "end": 4},
|
||||
IMAP{"text": "a", "begin": 0, "end": 1},
|
||||
IMAP{"text": "c", "begin": 2, "end": 3},
|
||||
},
|
||||
}},
|
||||
} {
|
||||
module(t, "text").call("re_find", d.pattern, d.text, d.count).expect(d.expected, "pattern: %q, text: %q", d.pattern, d.text)
|
||||
module(t, "text").call("re_compile", d.pattern).call("find", d.text, d.count).expect(d.expected, "pattern: %q, text: %q", d.pattern, d.text)
|
||||
}
|
||||
|
||||
// re_replace(pattern, text, repl)
|
||||
for _, d := range []struct {
|
||||
pattern string
|
||||
text string
|
||||
repl string
|
||||
expected interface{}
|
||||
}{
|
||||
{"a", "", "b", ""},
|
||||
{"a", "a", "b", "b"},
|
||||
{"a", "acac", "b", "bcbc"},
|
||||
{"a", "acac", "123", "123c123c"},
|
||||
{"ac", "acac", "99", "9999"},
|
||||
{"ac$", "acac", "foo", "acfoo"},
|
||||
} {
|
||||
module(t, "text").call("re_replace", d.pattern, d.text, d.repl).expect(d.expected, "pattern: %q, text: %q, repl: %q", d.pattern, d.text, d.repl)
|
||||
module(t, "text").call("re_compile", d.pattern).call("replace", d.text, d.repl).expect(d.expected, "pattern: %q, text: %q, repl: %q", d.pattern, d.text, d.repl)
|
||||
}
|
||||
|
||||
// re_split(pattern, text)
|
||||
for _, d := range []struct {
|
||||
pattern string
|
||||
text string
|
||||
expected interface{}
|
||||
}{
|
||||
{"a", "", ARR{""}},
|
||||
{"a", "abcabc", ARR{"", "bc", "bc"}},
|
||||
{"ab", "abcabc", ARR{"", "c", "c"}},
|
||||
{"^a", "abcabc", ARR{"", "bcabc"}},
|
||||
} {
|
||||
module(t, "text").call("re_split", d.pattern, d.text).expect(d.expected, "pattern: %q, text: %q", d.pattern, d.text)
|
||||
module(t, "text").call("re_compile", d.pattern).call("split", d.text).expect(d.expected, "pattern: %q, text: %q", d.pattern, d.text)
|
||||
}
|
||||
|
||||
// re_split(pattern, text, count))
|
||||
for _, d := range []struct {
|
||||
pattern string
|
||||
text string
|
||||
count int
|
||||
expected interface{}
|
||||
}{
|
||||
{"a", "", -1, ARR{""}},
|
||||
{"a", "abcabc", -1, ARR{"", "bc", "bc"}},
|
||||
{"ab", "abcabc", -1, ARR{"", "c", "c"}},
|
||||
{"^a", "abcabc", -1, ARR{"", "bcabc"}},
|
||||
{"a", "abcabc", 0, ARR{}},
|
||||
{"a", "abcabc", 1, ARR{"abcabc"}},
|
||||
{"a", "abcabc", 2, ARR{"", "bcabc"}},
|
||||
{"a", "abcabc", 3, ARR{"", "bc", "bc"}},
|
||||
{"b", "abcabc", 1, ARR{"abcabc"}},
|
||||
{"b", "abcabc", 2, ARR{"a", "cabc"}},
|
||||
{"b", "abcabc", 3, ARR{"a", "ca", "c"}},
|
||||
} {
|
||||
module(t, "text").call("re_split", d.pattern, d.text, d.count).expect(d.expected, "pattern: %q, text: %q", d.pattern, d.text)
|
||||
module(t, "text").call("re_compile", d.pattern).call("split", d.text, d.count).expect(d.expected, "pattern: %q, text: %q", d.pattern, d.text)
|
||||
}
|
||||
}
|
||||
|
||||
func TestText(t *testing.T) {
|
||||
module(t, "text").call("compare", "", "").expect(0)
|
||||
module(t, "text").call("compare", "", "a").expect(-1)
|
||||
module(t, "text").call("compare", "a", "").expect(1)
|
||||
module(t, "text").call("compare", "a", "a").expect(0)
|
||||
module(t, "text").call("compare", "a", "b").expect(-1)
|
||||
module(t, "text").call("compare", "b", "a").expect(1)
|
||||
module(t, "text").call("compare", "abcde", "abcde").expect(0)
|
||||
module(t, "text").call("compare", "abcde", "abcdf").expect(-1)
|
||||
module(t, "text").call("compare", "abcdf", "abcde").expect(1)
|
||||
|
||||
module(t, "text").call("contains", "", "").expect(true)
|
||||
module(t, "text").call("contains", "", "a").expect(false)
|
||||
module(t, "text").call("contains", "a", "").expect(true)
|
||||
module(t, "text").call("contains", "a", "a").expect(true)
|
||||
module(t, "text").call("contains", "abcde", "a").expect(true)
|
||||
module(t, "text").call("contains", "abcde", "abcde").expect(true)
|
||||
module(t, "text").call("contains", "abc", "abcde").expect(false)
|
||||
module(t, "text").call("contains", "ab cd", "bc").expect(false)
|
||||
|
||||
module(t, "text").call("replace", "", "", "", -1).expect("")
|
||||
module(t, "text").call("replace", "abcd", "a", "x", -1).expect("xbcd")
|
||||
module(t, "text").call("replace", "aaaa", "a", "x", -1).expect("xxxx")
|
||||
module(t, "text").call("replace", "aaaa", "a", "x", 0).expect("aaaa")
|
||||
module(t, "text").call("replace", "aaaa", "a", "x", 2).expect("xxaa")
|
||||
module(t, "text").call("replace", "abcd", "bc", "x", -1).expect("axd")
|
||||
|
||||
module(t, "text").call("format_bool", true).expect("true")
|
||||
module(t, "text").call("format_bool", false).expect("false")
|
||||
module(t, "text").call("format_float", -19.84, 'f', -1, 64).expect("-19.84")
|
||||
module(t, "text").call("format_int", -1984, 10).expect("-1984")
|
||||
module(t, "text").call("format_int", 1984, 8).expect("3700")
|
||||
module(t, "text").call("parse_bool", "true").expect(true)
|
||||
module(t, "text").call("parse_bool", "0").expect(false)
|
||||
module(t, "text").call("parse_float", "-19.84", 64).expect(-19.84)
|
||||
module(t, "text").call("parse_int", "-1984", 10, 64).expect(-1984)
|
||||
}
|
782
compiler/stdlib/times.go
Normal file
782
compiler/stdlib/times.go
Normal file
|
@ -0,0 +1,782 @@
|
|||
package stdlib
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
var timesModule = map[string]objects.Object{
|
||||
"format_ansic": &objects.String{Value: time.ANSIC},
|
||||
"format_unix_date": &objects.String{Value: time.UnixDate},
|
||||
"format_ruby_date": &objects.String{Value: time.RubyDate},
|
||||
"format_rfc822": &objects.String{Value: time.RFC822},
|
||||
"format_rfc822z": &objects.String{Value: time.RFC822Z},
|
||||
"format_rfc850": &objects.String{Value: time.RFC850},
|
||||
"format_rfc1123": &objects.String{Value: time.RFC1123},
|
||||
"format_rfc1123z": &objects.String{Value: time.RFC1123Z},
|
||||
"format_rfc3339": &objects.String{Value: time.RFC3339},
|
||||
"format_rfc3339_nano": &objects.String{Value: time.RFC3339Nano},
|
||||
"format_kitchen": &objects.String{Value: time.Kitchen},
|
||||
"format_stamp": &objects.String{Value: time.Stamp},
|
||||
"format_stamp_milli": &objects.String{Value: time.StampMilli},
|
||||
"format_stamp_micro": &objects.String{Value: time.StampMicro},
|
||||
"format_stamp_nano": &objects.String{Value: time.StampNano},
|
||||
"nanosecond": &objects.Int{Value: int64(time.Nanosecond)},
|
||||
"microsecond": &objects.Int{Value: int64(time.Microsecond)},
|
||||
"millisecond": &objects.Int{Value: int64(time.Millisecond)},
|
||||
"second": &objects.Int{Value: int64(time.Second)},
|
||||
"minute": &objects.Int{Value: int64(time.Minute)},
|
||||
"hour": &objects.Int{Value: int64(time.Hour)},
|
||||
"january": &objects.Int{Value: int64(time.January)},
|
||||
"february": &objects.Int{Value: int64(time.February)},
|
||||
"march": &objects.Int{Value: int64(time.March)},
|
||||
"april": &objects.Int{Value: int64(time.April)},
|
||||
"may": &objects.Int{Value: int64(time.May)},
|
||||
"june": &objects.Int{Value: int64(time.June)},
|
||||
"july": &objects.Int{Value: int64(time.July)},
|
||||
"august": &objects.Int{Value: int64(time.August)},
|
||||
"september": &objects.Int{Value: int64(time.September)},
|
||||
"october": &objects.Int{Value: int64(time.October)},
|
||||
"november": &objects.Int{Value: int64(time.November)},
|
||||
"december": &objects.Int{Value: int64(time.December)},
|
||||
"sleep": &objects.UserFunction{Value: timesSleep}, // sleep(int)
|
||||
"parse_duration": &objects.UserFunction{Value: timesParseDuration}, // parse_duration(str) => int
|
||||
"since": &objects.UserFunction{Value: timesSince}, // since(time) => int
|
||||
"until": &objects.UserFunction{Value: timesUntil}, // until(time) => int
|
||||
"duration_hours": &objects.UserFunction{Value: timesDurationHours}, // duration_hours(int) => float
|
||||
"duration_minutes": &objects.UserFunction{Value: timesDurationMinutes}, // duration_minutes(int) => float
|
||||
"duration_nanoseconds": &objects.UserFunction{Value: timesDurationNanoseconds}, // duration_nanoseconds(int) => int
|
||||
"duration_seconds": &objects.UserFunction{Value: timesDurationSeconds}, // duration_seconds(int) => float
|
||||
"duration_string": &objects.UserFunction{Value: timesDurationString}, // duration_string(int) => string
|
||||
"month_string": &objects.UserFunction{Value: timesMonthString}, // month_string(int) => string
|
||||
"date": &objects.UserFunction{Value: timesDate}, // date(year, month, day, hour, min, sec, nsec) => time
|
||||
"now": &objects.UserFunction{Value: timesNow}, // now() => time
|
||||
"parse": &objects.UserFunction{Value: timesParse}, // parse(format, str) => time
|
||||
"unix": &objects.UserFunction{Value: timesUnix}, // unix(sec, nsec) => time
|
||||
"add": &objects.UserFunction{Value: timesAdd}, // add(time, int) => time
|
||||
"add_date": &objects.UserFunction{Value: timesAddDate}, // add_date(time, years, months, days) => time
|
||||
"sub": &objects.UserFunction{Value: timesSub}, // sub(t time, u time) => int
|
||||
"after": &objects.UserFunction{Value: timesAfter}, // after(t time, u time) => bool
|
||||
"before": &objects.UserFunction{Value: timesBefore}, // before(t time, u time) => bool
|
||||
"time_year": &objects.UserFunction{Value: timesTimeYear}, // time_year(time) => int
|
||||
"time_month": &objects.UserFunction{Value: timesTimeMonth}, // time_month(time) => int
|
||||
"time_day": &objects.UserFunction{Value: timesTimeDay}, // time_day(time) => int
|
||||
"time_weekday": &objects.UserFunction{Value: timesTimeWeekday}, // time_weekday(time) => int
|
||||
"time_hour": &objects.UserFunction{Value: timesTimeHour}, // time_hour(time) => int
|
||||
"time_minute": &objects.UserFunction{Value: timesTimeMinute}, // time_minute(time) => int
|
||||
"time_second": &objects.UserFunction{Value: timesTimeSecond}, // time_second(time) => int
|
||||
"time_nanosecond": &objects.UserFunction{Value: timesTimeNanosecond}, // time_nanosecond(time) => int
|
||||
"time_unix": &objects.UserFunction{Value: timesTimeUnix}, // time_unix(time) => int
|
||||
"time_unix_nano": &objects.UserFunction{Value: timesTimeUnixNano}, // time_unix_nano(time) => int
|
||||
"time_format": &objects.UserFunction{Value: timesTimeFormat}, // time_format(time, format) => string
|
||||
"time_location": &objects.UserFunction{Value: timesTimeLocation}, // time_location(time) => string
|
||||
"time_string": &objects.UserFunction{Value: timesTimeString}, // time_string(time) => string
|
||||
"is_zero": &objects.UserFunction{Value: timesIsZero}, // is_zero(time) => bool
|
||||
"to_local": &objects.UserFunction{Value: timesToLocal}, // to_local(time) => time
|
||||
"to_utc": &objects.UserFunction{Value: timesToUTC}, // to_utc(time) => time
|
||||
}
|
||||
|
||||
func timesSleep(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(time.Duration(i1))
|
||||
ret = objects.UndefinedValue
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesParseDuration(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
dur, err := time.ParseDuration(s1)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: int64(dur)}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesSince(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: int64(time.Since(t1))}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesUntil(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: int64(time.Until(t1))}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesDurationHours(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Float{Value: time.Duration(i1).Hours()}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesDurationMinutes(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Float{Value: time.Duration(i1).Minutes()}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesDurationNanoseconds(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: time.Duration(i1).Nanoseconds()}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesDurationSeconds(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Float{Value: time.Duration(i1).Seconds()}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesDurationString(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.String{Value: time.Duration(i1).String()}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesMonthString(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.String{Value: time.Month(i1).String()}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesDate(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 7 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
i4, ok := objects.ToInt(args[3])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
i5, ok := objects.ToInt(args[4])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
i6, ok := objects.ToInt(args[5])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
i7, ok := objects.ToInt(args[6])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Time{Value: time.Date(i1, time.Month(i2), i3, i4, i5, i6, i7, time.Now().Location())}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesNow(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 0 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Time{Value: time.Now()}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesParse(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
parsed, err := time.Parse(s1, s2)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Time{Value: parsed}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesUnix(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt64(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Time{Value: time.Unix(i1, i2)}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesAdd(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt64(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Time{Value: t1.Add(time.Duration(i2))}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesSub(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
t2, ok := objects.ToTime(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: int64(t1.Sub(t2))}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesAddDate(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 4 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i4, ok := objects.ToInt(args[3])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Time{Value: t1.AddDate(i2, i3, i4)}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesAfter(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
t2, ok := objects.ToTime(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
if t1.After(t2) {
|
||||
ret = objects.TrueValue
|
||||
} else {
|
||||
ret = objects.FalseValue
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesBefore(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
t2, ok := objects.ToTime(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
if t1.Before(t2) {
|
||||
ret = objects.TrueValue
|
||||
} else {
|
||||
ret = objects.FalseValue
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesTimeYear(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: int64(t1.Year())}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesTimeMonth(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: int64(t1.Month())}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesTimeDay(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: int64(t1.Day())}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesTimeWeekday(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: int64(t1.Weekday())}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesTimeHour(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: int64(t1.Hour())}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesTimeMinute(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: int64(t1.Minute())}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesTimeSecond(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: int64(t1.Second())}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesTimeNanosecond(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: int64(t1.Nanosecond())}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesTimeUnix(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: int64(t1.Unix())}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesTimeUnixNano(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: int64(t1.UnixNano())}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesTimeFormat(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.String{Value: t1.Format(s2)}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesIsZero(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
if t1.IsZero() {
|
||||
ret = objects.TrueValue
|
||||
} else {
|
||||
ret = objects.FalseValue
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesToLocal(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Time{Value: t1.Local()}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesToUTC(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Time{Value: t1.UTC()}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesTimeLocation(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.String{Value: t1.Location().String()}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func timesTimeString(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.String{Value: t1.String()}
|
||||
|
||||
return
|
||||
}
|
62
compiler/stdlib/times_test.go
Normal file
62
compiler/stdlib/times_test.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package stdlib_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/d5/tengo/assert"
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func TestTimes(t *testing.T) {
|
||||
time1 := time.Date(1982, 9, 28, 19, 21, 44, 999, time.Now().Location())
|
||||
time2 := time.Now()
|
||||
|
||||
module(t, "times").call("sleep", 1).expect(objects.UndefinedValue)
|
||||
|
||||
assert.True(t, module(t, "times").call("since", time.Now().Add(-time.Hour)).o.(*objects.Int).Value > 3600000000000)
|
||||
assert.True(t, module(t, "times").call("until", time.Now().Add(time.Hour)).o.(*objects.Int).Value < 3600000000000)
|
||||
|
||||
module(t, "times").call("parse_duration", "1ns").expect(1)
|
||||
module(t, "times").call("parse_duration", "1ms").expect(1000000)
|
||||
module(t, "times").call("parse_duration", "1h").expect(3600000000000)
|
||||
module(t, "times").call("duration_hours", 1800000000000).expect(0.5)
|
||||
module(t, "times").call("duration_minutes", 1800000000000).expect(30.0)
|
||||
module(t, "times").call("duration_nanoseconds", 100).expect(100)
|
||||
module(t, "times").call("duration_seconds", 1000000).expect(0.001)
|
||||
module(t, "times").call("duration_string", 1800000000000).expect("30m0s")
|
||||
|
||||
module(t, "times").call("month_string", 1).expect("January")
|
||||
module(t, "times").call("month_string", 12).expect("December")
|
||||
|
||||
module(t, "times").call("date", 1982, 9, 28, 19, 21, 44, 999).expect(time1)
|
||||
assert.True(t, module(t, "times").call("now").o.(*objects.Time).Value.Sub(time.Now()).Nanoseconds() < 100000000) // within 100ms
|
||||
parsed, _ := time.Parse(time.RFC3339, "1982-09-28T19:21:44+07:00")
|
||||
module(t, "times").call("parse", time.RFC3339, "1982-09-28T19:21:44+07:00").expect(parsed)
|
||||
module(t, "times").call("unix", 1234325, 94493).expect(time.Unix(1234325, 94493))
|
||||
|
||||
module(t, "times").call("add", time2, 3600000000000).expect(time2.Add(time.Duration(3600000000000)))
|
||||
module(t, "times").call("sub", time2, time2.Add(-time.Hour)).expect(3600000000000)
|
||||
module(t, "times").call("add_date", time2, 1, 2, 3).expect(time2.AddDate(1, 2, 3))
|
||||
module(t, "times").call("after", time2, time2.Add(time.Hour)).expect(false)
|
||||
module(t, "times").call("after", time2, time2.Add(-time.Hour)).expect(true)
|
||||
module(t, "times").call("before", time2, time2.Add(time.Hour)).expect(true)
|
||||
module(t, "times").call("before", time2, time2.Add(-time.Hour)).expect(false)
|
||||
|
||||
module(t, "times").call("time_year", time1).expect(time1.Year())
|
||||
module(t, "times").call("time_month", time1).expect(int(time1.Month()))
|
||||
module(t, "times").call("time_day", time1).expect(time1.Day())
|
||||
module(t, "times").call("time_hour", time1).expect(time1.Hour())
|
||||
module(t, "times").call("time_minute", time1).expect(time1.Minute())
|
||||
module(t, "times").call("time_second", time1).expect(time1.Second())
|
||||
module(t, "times").call("time_nanosecond", time1).expect(time1.Nanosecond())
|
||||
module(t, "times").call("time_unix", time1).expect(time1.Unix())
|
||||
module(t, "times").call("time_unix_nano", time1).expect(time1.UnixNano())
|
||||
module(t, "times").call("time_format", time1, time.RFC3339).expect(time1.Format(time.RFC3339))
|
||||
module(t, "times").call("is_zero", time1).expect(false)
|
||||
module(t, "times").call("is_zero", time.Time{}).expect(true)
|
||||
module(t, "times").call("to_local", time1).expect(time1.Local())
|
||||
module(t, "times").call("to_utc", time1).expect(time1.UTC())
|
||||
module(t, "times").call("time_location", time1).expect(time1.Location().String())
|
||||
module(t, "times").call("time_string", time1).expect(time1.String())
|
||||
}
|
|
@ -7,15 +7,17 @@
|
|||
- **Char**: character (`rune` in Go)
|
||||
- **Bytes**: byte array (`[]byte` in Go)
|
||||
- **Array**: objects array (`[]Object` in Go)
|
||||
- **ImmutableArray**: immutable object array (`[]Object` in Go)
|
||||
- **Map**: objects map with string keys (`map[string]Object` in Go)
|
||||
- **ImmutableMap**: immutable object map with string keys (`map[string]Object` in Go)
|
||||
- **Time**: time (`time.Time` in Go)
|
||||
- **Error**: an error with underlying Object value of any type
|
||||
- **Undefined**: undefined
|
||||
|
||||
## Type Conversion/Coercion Table
|
||||
|src\dst |Int |String |Float |Bool |Char |Bytes |Array |Map |IMap|Error |Undefined|
|
||||
|src\dst |Int |String |Float |Bool |Char |Bytes |Array |Map |Time |Error |Undefined|
|
||||
| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
|
||||
|Int | - |_strconv_ |float64(v)|!IsFalsy()| rune(v)|**X**|**X**|**X**|**X**|**X**|**X**|
|
||||
|Int | - |_strconv_ |float64(v)|!IsFalsy()| rune(v)|**X**|**X**|**X**|_time.Unix()_|**X**|**X**|
|
||||
|String |_strconv_| - |_strconv_|!IsFalsy()|**X**|[]byte(s)|**X**|**X**|**X**|**X**|**X**|
|
||||
|Float |int64(f) |_strconv_ | - |!IsFalsy()|**X**|**X**|**X**|**X**|**X**|**X**|**X**|
|
||||
|Bool |1 / 0 |"true" / "false"|**X** | - |**X**|**X**|**X**|**X**|**X**|**X**|**X**|
|
||||
|
@ -23,13 +25,15 @@
|
|||
|Bytes |**X** |string(y)|**X** |!IsFalsy()|**X**| - |**X**|**X**|**X**|**X**|**X**|
|
||||
|Array |**X** |"[...]" |**X** |!IsFalsy()|**X**|**X**| - |**X**|**X**|**X**|**X**|
|
||||
|Map |**X** |"{...}" |**X** |!IsFalsy()|**X**|**X**|**X**| - |**X**|**X**|**X**|
|
||||
|IMap |**X** |"{...}" |**X** |!IsFalsy()|**X**|**X**|**X**|**X**| - |**X**|**X**|
|
||||
|Time |**X** |String() |**X** |!IsFalsy()|**X**|**X**|**X**|**X**| - |**X**|**X**|
|
||||
|Error |**X** |"error: ..." |**X** |false|**X**|**X**|**X**|**X**|**X**| - |**X**|
|
||||
|Undefined|**X** |**X**|**X** |false|**X**|**X**|**X**|**X**|**X**|**X**| - |
|
||||
|
||||
_* **X**: No conversion; Typed value functions for `script.Variable` will return zero values._
|
||||
_* strconv: converted using Go's conversion functions from `strconv` package._
|
||||
_* IsFalsy(): use [Object.IsFalsy()](#objectisfalsy) function_
|
||||
_* IsFalsy(): use [Object.IsFalsy()](#objectisfalsy) function_
|
||||
_* String(): use `Object.String()` function_
|
||||
_* time.Unix(): use `time.Unix(v, 0)` to convert to Time_
|
||||
|
||||
## Object.IsFalsy()
|
||||
|
||||
|
@ -43,7 +47,7 @@ _* IsFalsy(): use [Object.IsFalsy()](#objectisfalsy) function_
|
|||
- **Bytes**: `len(bytes) == 0`
|
||||
- **Array**: `len(arr) == 0`
|
||||
- **Map**: `len(map) == 0`
|
||||
- **ImmutableMap**: `len(map) == 0`
|
||||
- **Time**: `Time.IsZero()`
|
||||
- **Error**: `true` _(Error is always falsy)_
|
||||
- **Undefined**: `true` _(Undefined is always falsy)_
|
||||
|
||||
|
@ -56,6 +60,7 @@ _* IsFalsy(): use [Object.IsFalsy()](#objectisfalsy) function_
|
|||
- `char(x)`: tries to convert `x` into char; returns `undefined` if failed
|
||||
- `bytes(x)`: tries to convert `x` into bytes; returns `undefined` if failed
|
||||
- `bytes(N)`: as a special case this will create a Bytes variable with the given size `N` (only if `N` is int)
|
||||
- `time(x)`: tries to convert `x` into time; returns `undefined` if failed
|
||||
|
||||
## Type Checking Builtin Functions
|
||||
|
||||
|
@ -65,5 +70,10 @@ _* IsFalsy(): use [Object.IsFalsy()](#objectisfalsy) function_
|
|||
- `is_float(x)`: returns `true` if `x` is float; `false` otherwise
|
||||
- `is_char(x)`: returns `true` if `x` is char; `false` otherwise
|
||||
- `is_bytes(x)`: returns `true` if `x` is bytes; `false` otherwise
|
||||
- `is_array(x)`: return `true` if `x` is array; `false` otherwise
|
||||
- `is_immutable_array(x)`: return `true` if `x` is immutable array; `false` otherwise
|
||||
- `is_map(x)`: return `true` if `x` is map; `false` otherwise
|
||||
- `is_immutable_map(x)`: return `true` if `x` is immutable map; `false` otherwise
|
||||
- `is_time(x)`: return `true` if `x` is time; `false` otherwise
|
||||
- `is_error(x)`: returns `true` if `x` is error; `false` otherwise
|
||||
- `is_undefined(x)`: returns `true` if `x` is undefined; `false` otherwise
|
|
@ -1,28 +0,0 @@
|
|||
# Module - "exec"
|
||||
|
||||
```golang
|
||||
exec := import("exec")
|
||||
```
|
||||
|
||||
## Module Functions
|
||||
|
||||
- `look_path(file string) => string/error`: port of `exec.LookPath` function
|
||||
- `command(name string, args array(string)) => `Cmd/error`: port of `exec.Command` function
|
||||
|
||||
## Cmd Functions
|
||||
|
||||
```golang
|
||||
cmd := exec.command("echo", ["foo", "bar"])
|
||||
output := cmd.output()
|
||||
```
|
||||
|
||||
- `combined_output() => bytes/error`: port of `exec.Cmd.CombinedOutput` function
|
||||
- `output() => bytes/error`: port of `exec.Cmd.Output` function
|
||||
- `combined_output() => bytes/error`: port of `exec.Cmd.CombinedOutput` function
|
||||
- `run() => error`: port of `exec.Cmd.Run` function
|
||||
- `start() => error`: port of `exec.Cmd.Start` function
|
||||
- `wait() => error`: port of `exec.Cmd.Wait` function
|
||||
- `set_path(path string)`: sets `Path` of `exec.Cmd`
|
||||
- `set_dir(dir string)`: sets `Dir` of `exec.Cmd`
|
||||
- `set_env(env array(string))`: sets `Env` of `exec.Cmd`
|
||||
- `process() => Process`: returns Process (`Process` of `exec.Cmd`)
|
|
@ -4,76 +4,72 @@
|
|||
math := import("math")
|
||||
```
|
||||
|
||||
## Variables
|
||||
## Constants
|
||||
|
||||
- `e`: equivalent of Go's `math.E`
|
||||
- `pi`: equivalent of Go's `math.Pi`
|
||||
- `phi`: equivalent of Go's `math.Phi`
|
||||
- `sqrt2`: equivalent of Go's `math.Sqrt2`
|
||||
- `sqrtE`: equivalent of Go's `math.SqrtE`
|
||||
- `sprtPi`: equivalent of Go's `math.SqrtPi`
|
||||
- `sqrtPhi`: equivalent of Go's `math.SqrtPhi`
|
||||
- `ln2`: equivalent of Go's `math.Ln2`
|
||||
- `log2E`: equivalent of Go's `math.Log2E`
|
||||
- `ln10`: equivalent of Go's `math.Ln10`
|
||||
- `ln10E`: equivalent of Go's `math.Log10E`
|
||||
- `e`
|
||||
- `pi`
|
||||
- `phi`
|
||||
- `sqrt2`
|
||||
- `sqrtE`
|
||||
- `sprtPi`
|
||||
- `sqrtPhi`
|
||||
- `ln2`
|
||||
- `log2E`
|
||||
- `ln10`
|
||||
- `ln10E`
|
||||
|
||||
## Functions
|
||||
|
||||
- `abs(float) => float`: port of Go's `math.Abs` function
|
||||
- `acos(float) => float`: port of Go's `math.Acos` function
|
||||
- `acosh(float) => float`: port of Go's `math.Acosh` function
|
||||
- `asin(float) => float`: port of Go's `math.Asin` function
|
||||
- `asinh(float) => float`: port of Go's `math.Asinh` function
|
||||
- `atan(float) => float`: port of Go's `math.Atan` function
|
||||
- `atan2(float, float) => float`: port of Go's `math.Atan2` function
|
||||
- `atanh(float) => float`: port of Go's `math.Atanh` function
|
||||
- `cbrt(float) => float`: port of Go's `math.Cbrt` function
|
||||
- `ceil(float) => float`: port of Go's `math.Ceil` function
|
||||
- `copysign(float, float) => float`: port of Go's `math.Copysign` function
|
||||
- `cos(float) => float`: port of Go's `math.Cos` function
|
||||
- `cosh(float) => float`: port of Go's `math.Cosh` function
|
||||
- `dim(float, float) => float`: port of Go's `math.Dim` function
|
||||
- `erf(float) => float`: port of Go's `math.Erf` function
|
||||
- `erfc(float) => float`: port of Go's `math.Erfc` function
|
||||
- `erfcinv(float) => float`: port of Go's `math.Erfcinv` function
|
||||
- `erfinv(float) => float`: port of Go's `math.Erfinv` function
|
||||
- `exp(float) => float`: port of Go's `math.Exp` function
|
||||
- `exp2(float) => float`: port of Go's `math.Exp2` function
|
||||
- `expm1(float) => float`: port of Go's `math.Expm1` function
|
||||
- `floor(float) => float`: port of Go's `math.Floor` function
|
||||
- `gamma(float) => float`: port of Go's `math.Gamma` function
|
||||
- `hypot(float, float) => float`: port of Go's `math.Hypot` function
|
||||
- `ilogb(float) => float`: port of Go's `math.Ilogb` function
|
||||
- `inf(int) => float`: port of Go's `math.Inf` function
|
||||
- `is_inf(float, int) => float`: port of Go's `math.IsInf` function
|
||||
- `is_nan(float) => float`: port of Go's `math.IsNaN` function
|
||||
- `j0(float) => float`: port of Go's `math.J0` function
|
||||
- `j1(float) => float`: port of Go's `math.J1` function
|
||||
- `jn(int, float) => float`: port of Go's `math.Jn` function
|
||||
- `ldexp(float, int) => float`: port of Go's `math.Ldexp` function
|
||||
- `log(float) => float`: port of Go's `math.Log` function
|
||||
- `log10(float) => float`: port of Go's `math.Log10` function
|
||||
- `log1p(float) => float`: port of Go's `math.Log1p` function
|
||||
- `log2(float) => float`: port of Go's `math.Log2` function
|
||||
- `logb(float) => float`: port of Go's `math.Logb` function
|
||||
- `max(float, float) => float`: port of Go's `math.Max` function
|
||||
- `min(float, float) => float`: port of Go's `math.Min` function
|
||||
- `mod(float, float) => float`: port of Go's `math.Mod` function
|
||||
- `nan() => float`: port of Go's `math.NaN` function
|
||||
- `nextafter(float, float) => float`: port of Go's `math.Nextafter` function
|
||||
- `pow(float, float) => float`: port of Go's `math.Pow` function
|
||||
- `pow10(int) => float`: port of Go's `math.Pow10` function
|
||||
- `remainder(float, float) => float`: port of Go's `math.Remainder` function
|
||||
- `round(float) => float`: port of Go's `math.Round` function
|
||||
- `round_to_even(float) => float`: port of Go's `math.RoundToEven` function
|
||||
- `signbit(float) => float`: port of Go's `math.Signbit` function
|
||||
- `sin(float) => float`: port of Go's `math.Sin` function
|
||||
- `sinh(float) => float`: port of Go's `math.Sinh` function
|
||||
- `sqrt(float) => float`: port of Go's `math.Sqrt` function
|
||||
- `tan(float) => float`: port of Go's `math.Tan` function
|
||||
- `tanh(float) => float`: port of Go's `math.Tanh` function
|
||||
- `runct(float) => float`: port of Go's `math.Trunc` function
|
||||
- `y0(float) => float`: port of Go's `math.Y0` function
|
||||
- `y1(float) => float`: port of Go's `math.Y1` function
|
||||
- `yn(int, float) => float`: port of Go's `math.Yn` function
|
||||
- `abs(x float) => float`: returns the absolute value of x.
|
||||
- `acos(x float) => float`: returns the arccosine, in radians, of x.
|
||||
- `acosh(x float) => float`: returns the inverse hyperbolic cosine of x.
|
||||
- `asin(x float) => float`: returns the arcsine, in radians, of x.
|
||||
- `asinh(x float) => float`: returns the inverse hyperbolic sine of x.
|
||||
- `atan(x float) => float`: returns the arctangent, in radians, of x.
|
||||
- `atan2(y float, xfloat) => float`: returns the arc tangent of y/x, using the signs of the two to determine the quadrant of the return value.
|
||||
- `atanh(x float) => float`: returns the inverse hyperbolic tangent of x.
|
||||
- `cbrt(x float) => float`: returns the cube root of x.
|
||||
- `ceil(x float) => float`: returns the least integer value greater than or equal to x.
|
||||
- `copysign(x float, y float) => float`: returns a value with the magnitude of x and the sign of y.
|
||||
- `cos(x float) => float`: returns the cosine of the radian argument x.
|
||||
- `cosh(x float) => float`: returns the hyperbolic cosine of x.
|
||||
- `dim(x float, y float) => float`: returns the maximum of x-y or 0.
|
||||
- `erf(x float) => float`: returns the error function of x.
|
||||
- `erfc(x float) => float`: returns the complementary error function of x.
|
||||
- `exp(x float) => float`: returns e**x, the base-e exponential of x.
|
||||
- `exp2(x float) => float`: returns 2**x, the base-2 exponential of x.
|
||||
- `expm1(x float) => float`: returns e**x - 1, the base-e exponential of x minus 1. It is more accurate than Exp(x) - 1 when x is near zero.
|
||||
- `floor(x float) => float`: returns the greatest integer value less than or equal to x.
|
||||
- `gamma(x float) => float`: returns the Gamma function of x.
|
||||
- `hypot(p float, q float) => float`: returns Sqrt(p * p + q * q), taking care to avoid unnecessary overflow and underflow.
|
||||
- `ilogb(x float) => float`: returns the binary exponent of x as an integer.
|
||||
- `inf(sign int) => float`: returns positive infinity if sign >= 0, negative infinity if sign < 0.
|
||||
- `is_inf(f float, sign int) => float`: reports whether f is an infinity, according to sign. If sign > 0, IsInf reports whether f is positive infinity. If sign < 0, IsInf reports whether f is negative infinity. If sign == 0, IsInf reports whether f is either infinity.
|
||||
- `is_nan(f float) => float`: reports whether f is an IEEE 754 ``not-a-number'' value.
|
||||
- `j0(x float) => float`: returns the order-zero Bessel function of the first kind.
|
||||
- `j1(x float) => float`: returns the order-one Bessel function of the first kind.
|
||||
- `jn(n int, x float) => float`: returns the order-n Bessel function of the first kind.
|
||||
- `ldexp(frac float, exp int) => float`: is the inverse of frexp. It returns frac × 2**exp.
|
||||
- `log(x float) => float`: returns the natural logarithm of x.
|
||||
- `log10(x float) => float`: returns the decimal logarithm of x.
|
||||
- `log1p(x float) => float`: returns the natural logarithm of 1 plus its argument x. It is more accurate than Log(1 + x) when x is near zero.
|
||||
- `log2(x float) => float`: returns the binary logarithm of x.
|
||||
- `logb(x float) => float`: returns the binary exponent of x.
|
||||
- `max(x float, y float) => float`: returns the larger of x or y.
|
||||
- `min(x float, y float) => float`: returns the smaller of x or y.
|
||||
- `mod(x float, y float) => float`: returns the floating-point remainder of x/y.
|
||||
- `nan() => float`: returns an IEEE 754 ``not-a-number'' value.
|
||||
- `nextafter(x float, y float) => float`: returns the next representable float64 value after x towards y.
|
||||
- `pow(x float, y float) => float`: returns x**y, the base-x exponential of y.
|
||||
- `pow10(n int) => float`: returns 10**n, the base-10 exponential of n.
|
||||
- `remainder(x float, y float) => float`: returns the IEEE 754 floating-point remainder of x/y.
|
||||
- `signbit(x float) => float`: returns true if x is negative or negative zero.
|
||||
- `sin(x float) => float`: returns the sine of the radian argument x.
|
||||
- `sinh(x float) => float`: returns the hyperbolic sine of x.
|
||||
- `sqrt(x float) => float`: returns the square root of x.
|
||||
- `tan(x float) => float`: returns the tangent of the radian argument x.
|
||||
- `tanh(x float) => float`: returns the hyperbolic tangent of x.
|
||||
- `trunc(x float) => float`: returns the integer value of x.
|
||||
- `y0(x float) => float`: returns the order-zero Bessel function of the second kind.
|
||||
- `y1(x float) => float`: returns the order-one Bessel function of the second kind.
|
||||
- `yn(n int, x float) => float`: returns the order-n Bessel function of the second kind.
|
|
@ -4,82 +4,84 @@
|
|||
os := import("os")
|
||||
```
|
||||
|
||||
## Module Variables
|
||||
## Constants
|
||||
|
||||
- `o_rdonly`: equivalent of Go's `os.O_RDONLY`
|
||||
- `o_wronly`: equivalent of Go's `os.O_WRONLY`
|
||||
- `o_rdwr`: equivalent of Go's `os.O_RDWR`
|
||||
- `o_append`: equivalent of Go's `os.O_APPEND`
|
||||
- `o_create`: equivalent of Go's `os.O_CREATE`
|
||||
- `o_excl`: equivalent of Go's `os.O_EXCL`
|
||||
- `o_sync`: equivalent of Go's `os.O_SYNC`
|
||||
- `o_trunc`: equivalent of Go's `os.O_TRUNC`
|
||||
- `mode_dir`: equivalent of Go's `os.ModeDir`
|
||||
- `mode_append`: equivalent of Go's `os.ModeAppend`
|
||||
- `mode_exclusive`: equivalent of Go's `os.ModeExclusive`
|
||||
- `mode_temporary`: equivalent of Go's `os.ModeTemporary`
|
||||
- `mode_symlink`: equivalent of Go's `os.ModeSymlink`
|
||||
- `mode_device`: equivalent of Go's `os.ModeDevice`
|
||||
- `mode_named_pipe`: equivalent of Go's `os.ModeNamedPipe`
|
||||
- `mode_socket`: equivalent of Go's `os.ModeSocket`
|
||||
- `mode_setuid`: equivalent of Go's `os.ModeSetuid`
|
||||
- `mode_setgui`: equivalent of Go's `os.ModeSetgid`
|
||||
- `mode_char_device`: equivalent of Go's `os.ModeCharDevice`
|
||||
- `mode_sticky`: equivalent of Go's `os.ModeSticky`
|
||||
- `mode_irregular`: equivalent of Go's `os.ModeIrregular`
|
||||
- `mode_type`: equivalent of Go's `os.ModeType`
|
||||
- `mode_perm`: equivalent of Go's `os.ModePerm`
|
||||
- `seek_set`: equivalent of Go's `os.SEEK_SET`
|
||||
- `seek_cur`: equivalent of Go's `os.SEEK_CUR`
|
||||
- `seek_end`: equivalent of Go's `os.SEEK_END`
|
||||
- `path_separator`: equivalent of Go's `os.PathSeparator`
|
||||
- `path_list_separator`: equivalent of Go's `os.PathListSeparator`
|
||||
- `dev_null`: equivalent of Go's `os.DevNull`
|
||||
- `o_rdonly`
|
||||
- `o_wronly`
|
||||
- `o_rdwr`
|
||||
- `o_append`
|
||||
- `o_create`
|
||||
- `o_excl`
|
||||
- `o_sync`
|
||||
- `o_trunc`
|
||||
- `mode_dir`
|
||||
- `mode_append`
|
||||
- `mode_exclusive`
|
||||
- `mode_temporary`
|
||||
- `mode_symlink`
|
||||
- `mode_device`
|
||||
- `mode_named_pipe`
|
||||
- `mode_socket`
|
||||
- `mode_setuid`
|
||||
- `mode_setgui`
|
||||
- `mode_char_device`
|
||||
- `mode_sticky`
|
||||
- `mode_irregular`
|
||||
- `mode_type`
|
||||
- `mode_perm`
|
||||
- `seek_set`
|
||||
- `seek_cur`
|
||||
- `seek_end`
|
||||
- `path_separator`
|
||||
- `path_list_separator`
|
||||
- `dev_null`
|
||||
|
||||
## Module Functions
|
||||
|
||||
- `args() => array(string)`: returns `os.Args`
|
||||
- `chdir(dir string) => error`: port of `os.Chdir` function
|
||||
- `chmod(name string, mode int) => error `: port of Go's `os.Chmod` function
|
||||
- `chown(name string, uid int, gid int) => error `: port of Go's `os.Chown` function
|
||||
- `clearenv() `: port of Go's `os.Clearenv` function
|
||||
- `environ() => array(string) `: port of Go's `os.Environ` function
|
||||
- `executable() => string/error`: port of Go's `os.Executable()` function
|
||||
- `exit(code int) `: port of Go's `os.Exit` function
|
||||
- `expand_env(s string) => string `: port of Go's `os.ExpandEnv` function
|
||||
- `getegid() => int `: port of Go's `os.Getegid` function
|
||||
- `getenv(s string) => string `: port of Go's `os.Getenv` function
|
||||
- `geteuid() => int `: port of Go's `os.Geteuid` function
|
||||
- `getgid() => int `: port of Go's `os.Getgid` function
|
||||
- `getgroups() => array(string)/error `: port of Go's `os.Getgroups` function
|
||||
- `getpagesize() => int `: port of Go's `os.Getpagesize` function
|
||||
- `getpid() => int `: port of Go's `os.Getpid` function
|
||||
- `getppid() => int `: port of Go's `os.Getppid` function
|
||||
- `getuid() => int `: port of Go's `os.Getuid` function
|
||||
- `getwd() => string/error `: port of Go's `os.Getwd` function
|
||||
- `hostname() => string/error `: port of Go's `os.Hostname` function
|
||||
- `lchown(name string, uid int, gid int) => error `: port of Go's `os.Lchown` function
|
||||
- `link(oldname string, newname string) => error `: port of Go's `os.Link` function
|
||||
- `lookup_env(key string) => string/false`: port of Go's `os,LookupEnv` function
|
||||
- `mkdir(name string, perm int) => error `: port of Go's `os.Mkdir` function
|
||||
- `mkdir_all(name string, perm int) => error `: port of Go's `os.MkdirAll` function
|
||||
- `readlink(name string) => string/error `: port of Go's `os.Readlink` function
|
||||
- `remove(name string) => error `: port of Go's `os.Remove` function
|
||||
- `remove_all(name string) => error `: port of Go's `os.RemoveAll` function
|
||||
- `rename(oldpath string, newpath string) => error `: port of Go's `os.Rename` function
|
||||
- `setenv(key string, value string) => error `: port of Go's `os.Setenv` function
|
||||
- `symlink(oldname string newname string) => error `: port of Go's `os.Symlink` function
|
||||
- `temp_dir() => string `: port of Go's `os.TempDir` function
|
||||
- `truncate(name string, size int) => error `: port of Go's `os.Truncate` function
|
||||
- `unsetenv(key string) => error `: port of Go's `os.Unsetenv` function
|
||||
- `user_cache_dir() => string/error `: port of Go's `os.UserCacheDir` function
|
||||
- `create(name string) => File/error`: port of Go's `os.Create` function
|
||||
- `open(name string) => File/error`: port of Go's `os.Open` function
|
||||
- `open_file(name string, flag int, perm int) => File/error`: port of Go's `os.OpenFile` function
|
||||
- `find_process(pid int) => Process/error`: port of Go's `os.FindProcess` function
|
||||
- `start_process(name string, argv array(string), dir string, env array(string)) => Process/error`: port of Go's `os.StartProcess` function
|
||||
## Functions
|
||||
|
||||
## File Functions
|
||||
- `args() => [string]`: returns command-line arguments, starting with the program name.
|
||||
- `chdir(dir string) => error`: changes the current working directory to the named directory.
|
||||
- `chmod(name string, mode int) => error `: changes the mode of the named file to mode.
|
||||
- `chown(name string, uid int, gid int) => error `: changes the numeric uid and gid of the named file.
|
||||
- `clearenv()`: deletes all environment variables.
|
||||
- `environ() => [string] `: returns a copy of strings representing the environment.
|
||||
- `exit(code int) `: causes the current program to exit with the given status code.
|
||||
- `expand_env(s string) => string `: replaces ${var} or $var in the string according to the values of the current environment variables.
|
||||
- `getegid() => int `: returns the numeric effective group id of the caller.
|
||||
- `getenv(key string) => string `: retrieves the value of the environment variable named by the key.
|
||||
- `geteuid() => int `: returns the numeric effective user id of the caller.
|
||||
- `getgid() => int `: returns the numeric group id of the caller.
|
||||
- `getgroups() => [int]/error `: returns a list of the numeric ids of groups that the caller belongs to.
|
||||
- `getpagesize() => int `: returns the underlying system's memory page size.
|
||||
- `getpid() => int `: returns the process id of the caller.
|
||||
- `getppid() => int `: returns the process id of the caller's parent.
|
||||
- `getuid() => int `: returns the numeric user id of the caller.
|
||||
- `getwd() => string/error `: returns a rooted path name corresponding to the current directory.
|
||||
- `hostname() => string/error `: returns the host name reported by the kernel.
|
||||
- `lchown(name string, uid int, gid int) => error `: changes the numeric uid and gid of the named file.
|
||||
- `link(oldname string, newname string) => error `: creates newname as a hard link to the oldname file.
|
||||
- `lookup_env(key string) => string/false`: retrieves the value of the environment variable named by the key.
|
||||
- `mkdir(name string, perm int) => error `: creates a new directory with the specified name and permission bits (before umask).
|
||||
- `mkdir_all(name string, perm int) => error `: creates a directory named path, along with any necessary parents, and returns nil, or else returns an error.
|
||||
- `readlink(name string) => string/error `: returns the destination of the named symbolic link.
|
||||
- `remove(name string) => error `: removes the named file or (empty) directory.
|
||||
- `remove_all(name string) => error `: removes path and any children it contains.
|
||||
- `rename(oldpath string, newpath string) => error `: renames (moves) oldpath to newpath.
|
||||
- `setenv(key string, value string) => error `: sets the value of the environment variable named by the key.
|
||||
- `symlink(oldname string newname string) => error `: creates newname as a symbolic link to oldname.
|
||||
- `temp_dir() => string `: returns the default directory to use for temporary files.
|
||||
- `truncate(name string, size int) => error `: changes the size of the named file.
|
||||
- `unsetenv(key string) => error `: unsets a single environment variable.
|
||||
- `create(name string) => File/error`: creates the named file with mode 0666 (before umask), truncating it if it already exists.
|
||||
- `open(name string) => File/error`: opens the named file for reading. If successful, methods on the returned file can be used for reading; the associated file descriptor has mode O_RDONLY.
|
||||
- `open_file(name string, flag int, perm int) => File/error`: is the generalized open call; most users will use Open or Create instead. It opens the named file with specified flag (O_RDONLY etc.) and perm (before umask), if applicable.
|
||||
- `find_process(pid int) => Process/error`: looks for a running process by its pid.
|
||||
- `start_process(name string, argv [string], dir string, env [string]) => Process/error`: starts a new process with the program, arguments and attributes specified by name, argv and attr. The argv slice will become os.Args in the new process, so it normally starts with the program name.
|
||||
- `exec_look_path(file string) => string/error`: searches for an executable named file in the directories named by the PATH environment variable.
|
||||
- `exec(name string, args...) => Command/error`: returns the Command to execute the named program with the given arguments.
|
||||
|
||||
|
||||
## File
|
||||
|
||||
```golang
|
||||
file := os.create("myfile")
|
||||
|
@ -87,31 +89,31 @@ file.write_string("some data")
|
|||
file.close()
|
||||
```
|
||||
|
||||
- `chdir() => true/error`: port of `os.File.Chdir` function
|
||||
- `chown(uid int, gid int) => true/error`: port of `os.File.Chown` function
|
||||
- `close() => error`: port of `os.File.Close` function
|
||||
- `name() => string`: port of `os.File.Name` function
|
||||
- `readdirnames() => array(string)/error`: port of `os.File.Readdirnames` function
|
||||
- `sync() => error`: port of `os.File.Sync` function
|
||||
- `write(bytes) => int/error`: port of `os.File.Write` function
|
||||
- `write_string(string) => int/error`: port of `os.File.WriteString` function
|
||||
- `read(bytes) => int/error`: port of `os.File.Read` function
|
||||
- `chmod(mode int) => error`: port of `os.File.Chmod` function
|
||||
- `seek(offset int, whence int) => int/error`: port of `os.File.Seek` function
|
||||
- `chdir() => true/error`: changes the current working directory to the file,
|
||||
- `chown(uid int, gid int) => true/error`: changes the numeric uid and gid of the named file.
|
||||
- `close() => error`: closes the File, rendering it unusable for I/O.
|
||||
- `name() => string`: returns the name of the file as presented to Open.
|
||||
- `readdirnames(n int) => [string]/error`: reads and returns a slice of names from the directory.
|
||||
- `sync() => error`: commits the current contents of the file to stable storage.
|
||||
- `write(bytes) => int/error`: writes len(b) bytes to the File.
|
||||
- `write_string(string) => int/error`: is like 'write', but writes the contents of string s rather than a slice of bytes.
|
||||
- `read(bytes) => int/error`: reads up to len(b) bytes from the File.
|
||||
- `chmod(mode int) => error`: changes the mode of the file to mode.
|
||||
- `seek(offset int, whence int) => int/error`: sets the offset for the next Read or Write on file to offset, interpreted according to whence: 0 means relative to the origin of the file, 1 means relative to the current offset, and 2 means relative to the end.
|
||||
|
||||
## Process Functions
|
||||
## Process
|
||||
|
||||
```golang
|
||||
proc := start_process("app", ["arg1", "arg2"], "dir", [])
|
||||
proc.wait()
|
||||
```
|
||||
|
||||
- `kill() => error`: port of `os.Process.Kill` function
|
||||
- `release() => error`: port of `os.Process.Release` function
|
||||
- `signal(signal int) => error`: port of `os.Process.Signal` function
|
||||
- `wait() => ProcessState/error`: port of `os.Process.Wait` function
|
||||
- `kill() => error`: causes the Process to exit immediately.
|
||||
- `release() => error`: releases any resources associated with the process, rendering it unusable in the future.
|
||||
- `signal(signal int) => error`: sends a signal to the Process.
|
||||
- `wait() => ProcessState/error`: waits for the Process to exit, and then returns a ProcessState describing its status and an error, if any.
|
||||
|
||||
## ProcessState Functions
|
||||
## ProcessState
|
||||
|
||||
```golang
|
||||
proc := start_process("app", ["arg1", "arg2"], "dir", [])
|
||||
|
@ -119,7 +121,24 @@ stat := proc.wait()
|
|||
pid := stat.pid()
|
||||
```
|
||||
|
||||
- `exited() => bool`: port of `os.ProcessState.Exited` function
|
||||
- `pid() => int`: port of `os.ProcessState.Pid` function
|
||||
- `string() => string`: port of `os.ProcessState.String` function
|
||||
- `success() => bool`: port of `os.ProcessState.Success` function
|
||||
- `exited() => bool`: reports whether the program has exited.
|
||||
- `pid() => int`: returns the process id of the exited process.
|
||||
- `string() => string`: returns a string representation of the process.
|
||||
- `success() => bool`: reports whether the program exited successfully, such as with exit status 0 on Unix.
|
||||
|
||||
```golang
|
||||
cmd := exec.command("echo", ["foo", "bar"])
|
||||
output := cmd.output()
|
||||
```
|
||||
|
||||
## Command
|
||||
|
||||
- `combined_output() => bytes/error`: runs the command and returns its combined standard output and standard error.
|
||||
- `output() => bytes/error`: runs the command and returns its standard output.
|
||||
- `run() => error`: starts the specified command and waits for it to complete.
|
||||
- `start() => error`: starts the specified command but does not wait for it to complete.
|
||||
- `wait() => error`: waits for the command to exit and waits for any copying to stdin or copying from stdout or stderr to complete.
|
||||
- `set_path(path string)`: sets the path of the command to run.
|
||||
- `set_dir(dir string)`: sets the working directory of the process.
|
||||
- `set_env(env [string])`: sets the environment of the process.
|
||||
- `process() => Process`: returns the underlying process, once started.
|
58
docs/stdlib-text.md
Normal file
58
docs/stdlib-text.md
Normal file
|
@ -0,0 +1,58 @@
|
|||
# Module - "text"
|
||||
|
||||
```golang
|
||||
text := import("text")
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
- `re_match(pattern string, text string) => bool/error`: reports whether the string s contains any match of the regular expression pattern.
|
||||
- `re_find(pattern string, text string, count int) => [[{text: string, begin: int, end: int}]]/undefined`: returns an array holding all matches, each of which is an array of map object that contains matching text, begin and end (exclusive) index.
|
||||
- `re_replace(pattern string, text string, repl string) => string/error`: returns a copy of src, replacing matches of the pattern with the replacement string repl.
|
||||
- `re_split(pattern string, text string, count int) => [string]/error`: slices s into substrings separated by the expression and returns a slice of the substrings between those expression matches.
|
||||
- `re_compile(pattern string) => Regexp/error`: parses a regular expression and returns, if successful, a Regexp object that can be used to match against text.
|
||||
- `compare(a string, b string) => int`: returns an integer comparing two strings lexicographically. The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
|
||||
- `contains(s string, substr string) => bool`: reports whether substr is within s.
|
||||
- `contains_any(s string, chars string) => bool`: reports whether any Unicode code points in chars are within s.
|
||||
- `count(s string, substr string) => int`: counts the number of non-overlapping instances of substr in s.
|
||||
- `equal_fold(s string, t string) => bool`: reports whether s and t, interpreted as UTF-8 strings,
|
||||
- `fields(s string) => [string]`: splits the string s around each instance of one or more consecutive white space characters, as defined by unicode.IsSpace, returning a slice of substrings of s or an empty slice if s contains only white space.
|
||||
- `has_prefix(s string, prefix string) => bool`: tests whether the string s begins with prefix.
|
||||
- `has_suffix(s string, suffix string) => bool`: tests whether the string s ends with suffix.
|
||||
- `index(s string, substr string) => int`: returns the index of the first instance of substr in s, or -1 if substr is not present in s.
|
||||
- `index_any(s string, chars string) => int`: returns the index of the first instance of any Unicode code point from chars in s, or -1 if no Unicode code point from chars is present in s.
|
||||
- `join(arr string, sep string) => string`: concatenates the elements of a to create a single string. The separator string sep is placed between elements in the resulting string.
|
||||
- `last_index(s string, substr string) => int`: returns the index of the last instance of substr in s, or -1 if substr is not present in s.
|
||||
- `last_index_any(s string, chars string) => int`: returns the index of the last instance of any Unicode code point from chars in s, or -1 if no Unicode code point from chars is present in s.
|
||||
- `repeat(s string, count int) => string`: returns a new string consisting of count copies of the string s.
|
||||
- `replace(s string, old string, new string, n int) => string`: returns a copy of the string s with the first n non-overlapping instances of old replaced by new.
|
||||
- `split(s string, sep string) => [string]`: slices s into all substrings separated by sep and returns a slice of the substrings between those separators.
|
||||
- `split_after(s string, sep string) => [string]`: slices s into all substrings after each instance of sep and returns a slice of those substrings.
|
||||
- `split_after_n(s string, sep string, n int) => [string]`: slices s into substrings after each instance of sep and returns a slice of those substrings.
|
||||
- `split_n(s string, sep string, n int) => [string]`: slices s into substrings separated by sep and returns a slice of the substrings between those separators.
|
||||
- `title(s string) => string`: returns a copy of the string s with all Unicode letters that begin words mapped to their title case.
|
||||
- `to_lower(s string) => string`: returns a copy of the string s with all Unicode letters mapped to their lower case.
|
||||
- `to_title(s string) => string`: returns a copy of the string s with all Unicode letters mapped to their title case.
|
||||
- `to_upper(s string) => string`: returns a copy of the string s with all Unicode letters mapped to their upper case.
|
||||
- `trim_left(s string, cutset string) => string`: returns a slice of the string s with all leading Unicode code points contained in cutset removed.
|
||||
- `trim_prefix(s string, prefix string) => string`: returns s without the provided leading prefix string.
|
||||
- `trim_right(s string, cutset string) => string`: returns a slice of the string s, with all trailing Unicode code points contained in cutset removed.
|
||||
- `trim_space(s string) => string`: returns a slice of the string s, with all leading and trailing white space removed, as defined by Unicode.
|
||||
- `trim_suffix(s string, suffix string) => string`: returns s without the provided trailing suffix string.
|
||||
- `atoi(str string) => int/error`: returns the result of ParseInt(s, 10, 0) converted to type int.
|
||||
- `format_bool(b bool) => string`: returns "true" or "false" according to the value of b.
|
||||
- `format_float(f float, fmt string, prec int, bits int) => string`: converts the floating-point number f to a string, according to the format fmt and precision prec.
|
||||
- `format_int(i int, base int) => string`: returns the string representation of i in the given base, for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z' for digit values >= 10.
|
||||
- `itoa(i int) => string`: is shorthand for format_int(i, 10).
|
||||
- `parse_bool(s string) => bool/error`: returns the boolean value represented by the string. It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False. Any other value returns an error.
|
||||
- `parse_float(s string, bits int) => float/error`: converts the string s to a floating-point number with the precision specified by bitSize: 32 for float32, or 64 for float64. When bitSize=32, the result still has type float64, but it will be convertible to float32 without changing its value.
|
||||
- `parse_int(s string, base int, bits int) => int/error`: interprets a string s in the given base (0, 2 to 36) and bit size (0 to 64) and returns the corresponding value i.
|
||||
- `quote(s string) => string`: returns a double-quoted Go string literal representing s. The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for control characters and non-printable characters as defined by IsPrint.
|
||||
- `unquote(s string) => string/error`: interprets s as a single-quoted, double-quoted, or backquoted Go string literal, returning the string value that s quotes. (If s is single-quoted, it would be a Go character literal; Unquote returns the corresponding one-character string.)
|
||||
|
||||
## Regexp
|
||||
|
||||
- `match(text string) => bool`: reports whether the string s contains any match of the regular expression pattern.
|
||||
- `find(text string, count int) => [[{text: string, begin: int, end: int}]]/undefined`: returns an array holding all matches, each of which is an array of map object that contains matching text, begin and end (exclusive) index.
|
||||
- `replace(src string, repl string) => string`: returns a copy of src, replacing matches of the pattern with the replacement string repl.
|
||||
- `split(text string, count int) => [string]`: slices s into substrings separated by the expression and returns a slice of the substrings between those expression matches.
|
79
docs/stdlib-times.md
Normal file
79
docs/stdlib-times.md
Normal file
|
@ -0,0 +1,79 @@
|
|||
# Module - "times"
|
||||
|
||||
```golang
|
||||
times := import("times")
|
||||
```
|
||||
|
||||
## Constants
|
||||
|
||||
- `format_ansic`: time format "Mon Jan _2 15:04:05 2006"
|
||||
- `format_unix_date`: time format "Mon Jan _2 15:04:05 MST 2006"
|
||||
- `format_ruby_date`: time format "Mon Jan 02 15:04:05 -0700 2006"
|
||||
- `format_rfc822`: time format "02 Jan 06 15:04 MST"
|
||||
- `format_rfc822z`: time format "02 Jan 06 15:04 -0700"
|
||||
- `format_rfc850`: time format "Monday, 02-Jan-06 15:04:05 MST"
|
||||
- `format_rfc1123`: time format "Mon, 02 Jan 2006 15:04:05 MST"
|
||||
- `format_rfc1123z`: time format "Mon, 02 Jan 2006 15:04:05 -0700"
|
||||
- `format_rfc3339`: time format "2006-01-02T15:04:05Z07:00"
|
||||
- `format_rfc3339_nano`: time format "2006-01-02T15:04:05.999999999Z07:00"
|
||||
- `format_kitchen`: time format "3:04PM"
|
||||
- `format_stamp`: time format "Jan _2 15:04:05"
|
||||
- `format_stamp_milli`: time format "Jan _2 15:04:05.000"
|
||||
- `format_stamp_micro`: time format "Jan _2 15:04:05.000000"
|
||||
- `format_stamp_nano`: time format "Jan _2 15:04:05.000000000"
|
||||
- `nanosecond`
|
||||
- `microsecond`
|
||||
- `millisecond`
|
||||
- `second`
|
||||
- `minute`
|
||||
- `hour`
|
||||
- `january`
|
||||
- `february`
|
||||
- `march`
|
||||
- `april`
|
||||
- `may`
|
||||
- `june`
|
||||
- `july`
|
||||
- `august`
|
||||
- `september`
|
||||
- `october`
|
||||
- `november`
|
||||
- `december`
|
||||
|
||||
## Functions
|
||||
|
||||
- `sleep(duration int)`: pauses the current goroutine for at least the duration d. A negative or zero duration causes Sleep to return immediately.
|
||||
- `parse_duration(s string) => int`: parses a duration string. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
- `since(t time) => int`: returns the time elapsed since t.
|
||||
- `until(t time) => int`: returns the duration until t.
|
||||
- `duration_hours(duration int) => float`: returns the duration as a floating point number of hours.
|
||||
- `duration_minutes(duration int) => float`: returns the duration as a floating point number of minutes.
|
||||
- `duration_nanoseconds(duration int) => int`: returns the duration as an integer of nanoseconds.
|
||||
- `duration_seconds(duration int) => float`: returns the duration as a floating point number of seconds.
|
||||
- `duration_string(duration int) => string`: returns a string representation of duration.
|
||||
- `month_string(month int) => string`: returns the English name of the month ("January", "February", ...).
|
||||
- `date(year int, month int, day int, hour int, min int, sec int, nsec int) => time`: returns the Time corresponding to "yyyy-mm-dd hh:mm:ss + nsec nanoseconds". Current location is used.
|
||||
- `now() => time`: returns the current local time.
|
||||
- `parse(format string, s string) => time`: parses a formatted string and returns the time value it represents. The layout defines the format by showing how the reference time, defined to be "Mon Jan 2 15:04:05 -0700 MST 2006" would be interpreted if it were the value; it serves as an example of the input format. The same interpretation will then be made to the input string.
|
||||
- `unix(sec int, nsec int) => time`: returns the local Time corresponding to the given Unix time, sec seconds and nsec nanoseconds since January 1, 1970 UTC.
|
||||
- `add(t time, duration int) => time`: returns the time t+d.
|
||||
- `add_date(t time, years int, months int, days int) => time`: returns the time corresponding to adding the given number of years, months, and days to t. For example, AddDate(-1, 2, 3) applied to January 1, 2011 returns March 4, 2010.
|
||||
- `sub(t time, u time) => int`: returns the duration t-u.
|
||||
- `after(t time, u time) => bool`: reports whether the time instant t is after u.
|
||||
- `before(t time, u time) => bool`: reports whether the time instant t is before u.
|
||||
- `time_year(t time) => int`: returns the year in which t occurs.
|
||||
- `time_month(t time) => int`: returns the month of the year specified by t.
|
||||
- `time_day(t time) => int`: returns the day of the month specified by t.
|
||||
- `time_weekday(t time) => int`: returns the day of the week specified by t.
|
||||
- `time_hour(t time) => int`: returns the hour within the day specified by t, in the range [0, 23].
|
||||
- `time_minute(t time) => int`: returns the minute offset within the hour specified by t, in the range [0, 59].
|
||||
- `time_second(t time) => int`: returns the second offset within the minute specified by t, in the range [0, 59].
|
||||
- `time_nanosecond(t time) => int`: returns the nanosecond offset within the second specified by t, in the range [0, 999999999].
|
||||
- `time_unix(t time) => int`: returns t as a Unix time, the number of seconds elapsed since January 1, 1970 UTC. The result does not depend on the location associated with t.
|
||||
- `time_unix_nano(t time) => int`: returns t as a Unix time, the number of nanoseconds elapsed since January 1, 1970 UTC. The result is undefined if the Unix time in nanoseconds cannot be represented by an int64 (a date before the year 1678 or after 2262). Note that this means the result of calling UnixNano on the zero Time is undefined. The result does not depend on the location associated with t.
|
||||
- `time_format(t time, format) => string`: returns a textual representation of the time value formatted according to layout, which defines the format by showing how the reference time, defined to be "Mon Jan 2 15:04:05 -0700 MST 2006" would be displayed if it were the value; it serves as an example of the desired output. The same display rules will then be applied to the time value.
|
||||
- `time_location(t time) => string`: returns the time zone name associated with t.
|
||||
- `time_string(t time) => string`: returns the time formatted using the format string "2006-01-02 15:04:05.999999999 -0700 MST".
|
||||
- `is_zero(t time) => bool`: reports whether t represents the zero time instant, January 1, year 1, 00:00:00 UTC.
|
||||
- `to_local(t time) => time`: returns t with the location set to local time.
|
||||
- `to_utc(t time) => time`: returns t with the location set to UTC.
|
|
@ -1,7 +1,6 @@
|
|||
# Standard Library
|
||||
|
||||
_Warning: standard library implementations/interfaces are **experimental** and subject to change in the future release._
|
||||
|
||||
- [math](https://github.com/d5/tengo/blob/master/docs/stdlib-math.md)
|
||||
- [os](https://github.com/d5/tengo/blob/master/docs/stdlib-os.md)
|
||||
- [exec](https://github.com/d5/tengo/blob/master/docs/stdlib-exec.md)
|
||||
- [os](https://github.com/d5/tengo/blob/master/docs/stdlib-os.md): platform-independent interface to operating system functionality.
|
||||
- [text](https://github.com/d5/tengo/blob/master/docs/stdlib-text.md): regular expressions, string conversion, and manipulation
|
||||
- [math](https://github.com/d5/tengo/blob/master/docs/stdlib-math.md): mathematical constants and functions
|
||||
- [times](https://github.com/d5/tengo/blob/master/docs/stdlib-times.md): time-related functions
|
|
@ -6,11 +6,12 @@ import (
|
|||
|
||||
// Bool represents a boolean value.
|
||||
type Bool struct {
|
||||
Value bool
|
||||
// this is intentionally non-public to force using objects.TrueValue and FalseValue always
|
||||
value bool
|
||||
}
|
||||
|
||||
func (o *Bool) String() string {
|
||||
if o.Value {
|
||||
if o.value {
|
||||
return "true"
|
||||
}
|
||||
|
||||
|
@ -30,22 +31,34 @@ func (o *Bool) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
|||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *Bool) Copy() Object {
|
||||
v := Bool{Value: o.Value}
|
||||
return &v
|
||||
return o
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o *Bool) IsFalsy() bool {
|
||||
return !o.Value
|
||||
return !o.value
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *Bool) Equals(x Object) bool {
|
||||
t, ok := x.(*Bool)
|
||||
if !ok {
|
||||
return false
|
||||
return o == x
|
||||
}
|
||||
|
||||
// GobDecode decodes bool value from input bytes.
|
||||
func (o *Bool) GobDecode(b []byte) (err error) {
|
||||
o.value = b[0] == 1
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GobEncode encodes bool values into bytes.
|
||||
func (o *Bool) GobEncode() (b []byte, err error) {
|
||||
if o.value {
|
||||
b = []byte{1}
|
||||
} else {
|
||||
b = []byte{0}
|
||||
}
|
||||
|
||||
return o.Value == t.Value
|
||||
return
|
||||
}
|
||||
|
|
|
@ -77,7 +77,11 @@ func builtinBool(args ...Object) (Object, error) {
|
|||
|
||||
v, ok := ToBool(args[0])
|
||||
if ok {
|
||||
return &Bool{Value: v}, nil
|
||||
if v {
|
||||
return TrueValue, nil
|
||||
}
|
||||
|
||||
return FalseValue, nil
|
||||
}
|
||||
|
||||
return UndefinedValue, nil
|
||||
|
@ -127,3 +131,25 @@ func builtinBytes(args ...Object) (Object, error) {
|
|||
|
||||
return UndefinedValue, nil
|
||||
}
|
||||
|
||||
func builtinTime(args ...Object) (Object, error) {
|
||||
argsLen := len(args)
|
||||
if !(argsLen == 1 || argsLen == 2) {
|
||||
return nil, ErrWrongNumArguments
|
||||
}
|
||||
|
||||
if _, ok := args[0].(*Time); ok {
|
||||
return args[0], nil
|
||||
}
|
||||
|
||||
v, ok := ToTime(args[0])
|
||||
if ok {
|
||||
return &Time{Value: v}, nil
|
||||
}
|
||||
|
||||
if argsLen == 2 {
|
||||
return args[1], nil
|
||||
}
|
||||
|
||||
return UndefinedValue, nil
|
||||
}
|
||||
|
|
|
@ -72,6 +72,66 @@ func builtinIsBytes(args ...Object) (Object, error) {
|
|||
return FalseValue, nil
|
||||
}
|
||||
|
||||
func builtinIsArray(args ...Object) (Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, ErrWrongNumArguments
|
||||
}
|
||||
|
||||
if _, ok := args[0].(*Array); ok {
|
||||
return TrueValue, nil
|
||||
}
|
||||
|
||||
return FalseValue, nil
|
||||
}
|
||||
|
||||
func builtinIsImmutableArray(args ...Object) (Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, ErrWrongNumArguments
|
||||
}
|
||||
|
||||
if _, ok := args[0].(*ImmutableArray); ok {
|
||||
return TrueValue, nil
|
||||
}
|
||||
|
||||
return FalseValue, nil
|
||||
}
|
||||
|
||||
func builtinIsMap(args ...Object) (Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, ErrWrongNumArguments
|
||||
}
|
||||
|
||||
if _, ok := args[0].(*Map); ok {
|
||||
return TrueValue, nil
|
||||
}
|
||||
|
||||
return FalseValue, nil
|
||||
}
|
||||
|
||||
func builtinIsImmutableMap(args ...Object) (Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, ErrWrongNumArguments
|
||||
}
|
||||
|
||||
if _, ok := args[0].(*ImmutableMap); ok {
|
||||
return TrueValue, nil
|
||||
}
|
||||
|
||||
return FalseValue, nil
|
||||
}
|
||||
|
||||
func builtinIsTime(args ...Object) (Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, ErrWrongNumArguments
|
||||
}
|
||||
|
||||
if _, ok := args[0].(*Time); ok {
|
||||
return TrueValue, nil
|
||||
}
|
||||
|
||||
return FalseValue, nil
|
||||
}
|
||||
|
||||
func builtinIsError(args ...Object) (Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, ErrWrongNumArguments
|
||||
|
@ -89,7 +149,7 @@ func builtinIsUndefined(args ...Object) (Object, error) {
|
|||
return nil, ErrWrongNumArguments
|
||||
}
|
||||
|
||||
if _, ok := args[0].(*Undefined); ok {
|
||||
if args[0] == UndefinedValue {
|
||||
return TrueValue, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,10 @@ var Builtins = []NamedBuiltinFunc{
|
|||
Name: "bytes",
|
||||
Func: builtinBytes,
|
||||
},
|
||||
{
|
||||
Name: "time",
|
||||
Func: builtinTime,
|
||||
},
|
||||
{
|
||||
Name: "is_int",
|
||||
Func: builtinIsInt,
|
||||
|
@ -80,6 +84,26 @@ var Builtins = []NamedBuiltinFunc{
|
|||
Name: "is_bytes",
|
||||
Func: builtinIsBytes,
|
||||
},
|
||||
{
|
||||
Name: "is_array",
|
||||
Func: builtinIsArray,
|
||||
},
|
||||
{
|
||||
Name: "is_immutable_array",
|
||||
Func: builtinIsImmutableArray,
|
||||
},
|
||||
{
|
||||
Name: "is_map",
|
||||
Func: builtinIsMap,
|
||||
},
|
||||
{
|
||||
Name: "is_immutable_map",
|
||||
Func: builtinIsImmutableMap,
|
||||
},
|
||||
{
|
||||
Name: "is_time",
|
||||
Func: builtinIsTime,
|
||||
},
|
||||
{
|
||||
Name: "is_error",
|
||||
Func: builtinIsError,
|
||||
|
|
|
@ -3,11 +3,12 @@ package objects
|
|||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ToString will try to convert object o to string value.
|
||||
func ToString(o Object) (v string, ok bool) {
|
||||
if _, isUndefined := o.(*Undefined); isUndefined {
|
||||
if o == UndefinedValue {
|
||||
//ok = false
|
||||
return
|
||||
}
|
||||
|
@ -36,7 +37,7 @@ func ToInt(o Object) (v int, ok bool) {
|
|||
v = int(o.Value)
|
||||
ok = true
|
||||
case *Bool:
|
||||
if o.Value {
|
||||
if o == TrueValue {
|
||||
v = 1
|
||||
}
|
||||
ok = true
|
||||
|
@ -65,7 +66,7 @@ func ToInt64(o Object) (v int64, ok bool) {
|
|||
v = int64(o.Value)
|
||||
ok = true
|
||||
case *Bool:
|
||||
if o.Value {
|
||||
if o == TrueValue {
|
||||
v = 1
|
||||
}
|
||||
ok = true
|
||||
|
@ -140,6 +141,21 @@ func ToByteSlice(o Object) (v []byte, ok bool) {
|
|||
return
|
||||
}
|
||||
|
||||
// ToTime will try to convert object o to time.Time value.
|
||||
func ToTime(o Object) (v time.Time, ok bool) {
|
||||
switch o := o.(type) {
|
||||
case *Time:
|
||||
v = o.Value
|
||||
ok = true
|
||||
case *Int:
|
||||
v = time.Unix(o.Value, 0)
|
||||
ok = true
|
||||
}
|
||||
|
||||
//ok = false
|
||||
return
|
||||
}
|
||||
|
||||
// objectToInterface attempts to convert an object o to an interface{} value
|
||||
func objectToInterface(o Object) (res interface{}) {
|
||||
switch o := o.(type) {
|
||||
|
@ -150,7 +166,7 @@ func objectToInterface(o Object) (res interface{}) {
|
|||
case *Float:
|
||||
res = o.Value
|
||||
case *Bool:
|
||||
res = o.Value
|
||||
res = o == TrueValue
|
||||
case *Char:
|
||||
res = o.Value
|
||||
case *Bytes:
|
||||
|
@ -176,7 +192,7 @@ func objectToInterface(o Object) (res interface{}) {
|
|||
func FromInterface(v interface{}) (Object, error) {
|
||||
switch v := v.(type) {
|
||||
case nil:
|
||||
return &Undefined{}, nil
|
||||
return UndefinedValue, nil
|
||||
case string:
|
||||
return &String{Value: v}, nil
|
||||
case int64:
|
||||
|
@ -184,7 +200,10 @@ func FromInterface(v interface{}) (Object, error) {
|
|||
case int:
|
||||
return &Int{Value: int64(v)}, nil
|
||||
case bool:
|
||||
return &Bool{Value: v}, nil
|
||||
if v {
|
||||
return TrueValue, nil
|
||||
}
|
||||
return FalseValue, nil
|
||||
case rune:
|
||||
return &Char{Value: v}, nil
|
||||
case byte:
|
||||
|
@ -220,6 +239,8 @@ func FromInterface(v interface{}) (Object, error) {
|
|||
arr[i] = vo
|
||||
}
|
||||
return &Array{Value: arr}, nil
|
||||
case time.Time:
|
||||
return &Time{Value: v}, nil
|
||||
case Object:
|
||||
return v, nil
|
||||
}
|
||||
|
|
|
@ -41,28 +41,28 @@ func TestFloat_BinaryOp(t *testing.T) {
|
|||
// float < float
|
||||
for l := float64(-2); l <= 2.1; l += 0.4 {
|
||||
for r := float64(-2); r <= 2.1; r += 0.4 {
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.Less, &objects.Float{Value: r}, &objects.Bool{Value: l < r})
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.Less, &objects.Float{Value: r}, boolValue(l < r))
|
||||
}
|
||||
}
|
||||
|
||||
// float > float
|
||||
for l := float64(-2); l <= 2.1; l += 0.4 {
|
||||
for r := float64(-2); r <= 2.1; r += 0.4 {
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.Greater, &objects.Float{Value: r}, &objects.Bool{Value: l > r})
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.Greater, &objects.Float{Value: r}, boolValue(l > r))
|
||||
}
|
||||
}
|
||||
|
||||
// float <= float
|
||||
for l := float64(-2); l <= 2.1; l += 0.4 {
|
||||
for r := float64(-2); r <= 2.1; r += 0.4 {
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.LessEq, &objects.Float{Value: r}, &objects.Bool{Value: l <= r})
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.LessEq, &objects.Float{Value: r}, boolValue(l <= r))
|
||||
}
|
||||
}
|
||||
|
||||
// float >= float
|
||||
for l := float64(-2); l <= 2.1; l += 0.4 {
|
||||
for r := float64(-2); r <= 2.1; r += 0.4 {
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.GreaterEq, &objects.Float{Value: r}, &objects.Bool{Value: l >= r})
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.GreaterEq, &objects.Float{Value: r}, boolValue(l >= r))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,28 +99,28 @@ func TestFloat_BinaryOp(t *testing.T) {
|
|||
// float < int
|
||||
for l := float64(-2); l <= 2.1; l += 0.4 {
|
||||
for r := int64(-2); r <= 2; r++ {
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.Less, &objects.Int{Value: r}, &objects.Bool{Value: l < float64(r)})
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.Less, &objects.Int{Value: r}, boolValue(l < float64(r)))
|
||||
}
|
||||
}
|
||||
|
||||
// float > int
|
||||
for l := float64(-2); l <= 2.1; l += 0.4 {
|
||||
for r := int64(-2); r <= 2; r++ {
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.Greater, &objects.Int{Value: r}, &objects.Bool{Value: l > float64(r)})
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.Greater, &objects.Int{Value: r}, boolValue(l > float64(r)))
|
||||
}
|
||||
}
|
||||
|
||||
// float <= int
|
||||
for l := float64(-2); l <= 2.1; l += 0.4 {
|
||||
for r := int64(-2); r <= 2; r++ {
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.LessEq, &objects.Int{Value: r}, &objects.Bool{Value: l <= float64(r)})
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.LessEq, &objects.Int{Value: r}, boolValue(l <= float64(r)))
|
||||
}
|
||||
}
|
||||
|
||||
// float >= int
|
||||
for l := float64(-2); l <= 2.1; l += 0.4 {
|
||||
for r := int64(-2); r <= 2; r++ {
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.GreaterEq, &objects.Int{Value: r}, &objects.Bool{Value: l >= float64(r)})
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.GreaterEq, &objects.Int{Value: r}, boolValue(l >= float64(r)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,28 +114,28 @@ func TestInt_BinaryOp(t *testing.T) {
|
|||
// int < int
|
||||
for l := int64(-2); l <= 2; l++ {
|
||||
for r := int64(-2); r <= 2; r++ {
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.Less, &objects.Int{Value: r}, &objects.Bool{Value: l < r})
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.Less, &objects.Int{Value: r}, boolValue(l < r))
|
||||
}
|
||||
}
|
||||
|
||||
// int > int
|
||||
for l := int64(-2); l <= 2; l++ {
|
||||
for r := int64(-2); r <= 2; r++ {
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.Greater, &objects.Int{Value: r}, &objects.Bool{Value: l > r})
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.Greater, &objects.Int{Value: r}, boolValue(l > r))
|
||||
}
|
||||
}
|
||||
|
||||
// int <= int
|
||||
for l := int64(-2); l <= 2; l++ {
|
||||
for r := int64(-2); r <= 2; r++ {
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.LessEq, &objects.Int{Value: r}, &objects.Bool{Value: l <= r})
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.LessEq, &objects.Int{Value: r}, boolValue(l <= r))
|
||||
}
|
||||
}
|
||||
|
||||
// int >= int
|
||||
for l := int64(-2); l <= 2; l++ {
|
||||
for r := int64(-2); r <= 2; r++ {
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.GreaterEq, &objects.Int{Value: r}, &objects.Bool{Value: l >= r})
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.GreaterEq, &objects.Int{Value: r}, boolValue(l >= r))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,28 +172,28 @@ func TestInt_BinaryOp(t *testing.T) {
|
|||
// int < float
|
||||
for l := int64(-2); l <= 2; l++ {
|
||||
for r := float64(-2); r <= 2.1; r += 0.5 {
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.Less, &objects.Float{Value: r}, &objects.Bool{Value: float64(l) < r})
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.Less, &objects.Float{Value: r}, boolValue(float64(l) < r))
|
||||
}
|
||||
}
|
||||
|
||||
// int > float
|
||||
for l := int64(-2); l <= 2; l++ {
|
||||
for r := float64(-2); r <= 2.1; r += 0.5 {
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.Greater, &objects.Float{Value: r}, &objects.Bool{Value: float64(l) > r})
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.Greater, &objects.Float{Value: r}, boolValue(float64(l) > r))
|
||||
}
|
||||
}
|
||||
|
||||
// int <= float
|
||||
for l := int64(-2); l <= 2; l++ {
|
||||
for r := float64(-2); r <= 2.1; r += 0.5 {
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.LessEq, &objects.Float{Value: r}, &objects.Bool{Value: float64(l) <= r})
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.LessEq, &objects.Float{Value: r}, boolValue(float64(l) <= r))
|
||||
}
|
||||
}
|
||||
|
||||
// int >= float
|
||||
for l := int64(-2); l <= 2; l++ {
|
||||
for r := float64(-2); r <= 2.1; r += 0.5 {
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.GreaterEq, &objects.Float{Value: r}, &objects.Bool{Value: float64(l) >= r})
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.GreaterEq, &objects.Float{Value: r}, boolValue(float64(l) >= r))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@ package objects
|
|||
|
||||
var (
|
||||
// TrueValue represents a true value.
|
||||
TrueValue Object = &Bool{Value: true}
|
||||
TrueValue Object = &Bool{value: true}
|
||||
|
||||
// FalseValue represents a false value.
|
||||
FalseValue Object = &Bool{Value: false}
|
||||
FalseValue Object = &Bool{value: false}
|
||||
|
||||
// UndefinedValue represents an undefined value.
|
||||
UndefinedValue Object = &Undefined{}
|
||||
|
|
|
@ -15,3 +15,11 @@ func testBinaryOp(t *testing.T, lhs objects.Object, op token.Token, rhs objects.
|
|||
|
||||
return assert.NoError(t, err) && assert.Equal(t, expected, actual)
|
||||
}
|
||||
|
||||
func boolValue(b bool) objects.Object {
|
||||
if b {
|
||||
return objects.TrueValue
|
||||
}
|
||||
|
||||
return objects.FalseValue
|
||||
}
|
||||
|
|
89
objects/time.go
Normal file
89
objects/time.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
package objects
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// Time represents a time value.
|
||||
type Time struct {
|
||||
Value time.Time
|
||||
}
|
||||
|
||||
func (o *Time) String() string {
|
||||
return o.Value.String()
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o *Time) TypeName() string {
|
||||
return "time"
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (o *Time) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
switch rhs := rhs.(type) {
|
||||
case *Int:
|
||||
switch op {
|
||||
case token.Add: // time + int => time
|
||||
if rhs.Value == 0 {
|
||||
return o, nil
|
||||
}
|
||||
return &Time{Value: o.Value.Add(time.Duration(rhs.Value))}, nil
|
||||
case token.Sub: // time - int => time
|
||||
if rhs.Value == 0 {
|
||||
return o, nil
|
||||
}
|
||||
return &Time{Value: o.Value.Add(time.Duration(-rhs.Value))}, nil
|
||||
}
|
||||
case *Time:
|
||||
switch op {
|
||||
case token.Sub: // time - time => int (duration)
|
||||
return &Int{Value: int64(o.Value.Sub(rhs.Value))}, nil
|
||||
case token.Less: // time < time => bool
|
||||
if o.Value.Before(rhs.Value) {
|
||||
return TrueValue, nil
|
||||
}
|
||||
return FalseValue, nil
|
||||
case token.Greater:
|
||||
if o.Value.After(rhs.Value) {
|
||||
return TrueValue, nil
|
||||
}
|
||||
return FalseValue, nil
|
||||
case token.LessEq:
|
||||
if o.Value.Equal(rhs.Value) || o.Value.Before(rhs.Value) {
|
||||
return TrueValue, nil
|
||||
}
|
||||
return FalseValue, nil
|
||||
case token.GreaterEq:
|
||||
if o.Value.Equal(rhs.Value) || o.Value.After(rhs.Value) {
|
||||
return TrueValue, nil
|
||||
}
|
||||
return FalseValue, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *Time) Copy() Object {
|
||||
return &Time{Value: o.Value}
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o *Time) IsFalsy() bool {
|
||||
return o.Value.IsZero()
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *Time) Equals(x Object) bool {
|
||||
t, ok := x.(*Time)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return o.Value.Equal(t.Value)
|
||||
}
|
|
@ -22,7 +22,7 @@ func (o *Undefined) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
|||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *Undefined) Copy() Object {
|
||||
return &Undefined{}
|
||||
return o
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
|
@ -33,7 +33,5 @@ func (o *Undefined) IsFalsy() bool {
|
|||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *Undefined) Equals(x Object) bool {
|
||||
_, ok := x.(*Undefined)
|
||||
|
||||
return ok
|
||||
return o == x
|
||||
}
|
||||
|
|
|
@ -617,23 +617,21 @@ func (v *VM) Run() error {
|
|||
left := v.stack[v.sp-3]
|
||||
v.sp -= 3
|
||||
|
||||
var lowIdx, highIdx int64
|
||||
|
||||
switch low := (*low).(type) {
|
||||
case *objects.Undefined:
|
||||
//lowIdx = 0
|
||||
case *objects.Int:
|
||||
lowIdx = low.Value
|
||||
default:
|
||||
return fmt.Errorf("non-integer slice index: %s", low.TypeName())
|
||||
var lowIdx int64
|
||||
if *low != objects.UndefinedValue {
|
||||
if low, ok := (*low).(*objects.Int); ok {
|
||||
lowIdx = low.Value
|
||||
} else {
|
||||
return fmt.Errorf("non-integer slice index: %s", low.TypeName())
|
||||
}
|
||||
}
|
||||
|
||||
switch high := (*high).(type) {
|
||||
case *objects.Undefined:
|
||||
highIdx = -1 // will be replaced by number of elements
|
||||
case *objects.Int:
|
||||
var highIdx int64
|
||||
if *high == objects.UndefinedValue {
|
||||
highIdx = -1
|
||||
} else if high, ok := (*high).(*objects.Int); ok {
|
||||
highIdx = high.Value
|
||||
default:
|
||||
} else {
|
||||
return fmt.Errorf("non-integer slice index: %s", high.TypeName())
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@ package runtime_test
|
|||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func TestBuiltinFunction(t *testing.T) {
|
||||
|
@ -24,14 +26,14 @@ func TestBuiltinFunction(t *testing.T) {
|
|||
expect(t, `out = int(true)`, 1)
|
||||
expect(t, `out = int(false)`, 0)
|
||||
expect(t, `out = int('8')`, 56)
|
||||
expect(t, `out = int([1])`, undefined())
|
||||
expect(t, `out = int({a: 1})`, undefined())
|
||||
expect(t, `out = int(undefined)`, undefined())
|
||||
expect(t, `out = int([1])`, objects.UndefinedValue)
|
||||
expect(t, `out = int({a: 1})`, objects.UndefinedValue)
|
||||
expect(t, `out = int(undefined)`, objects.UndefinedValue)
|
||||
expect(t, `out = int("-522", 1)`, -522)
|
||||
expect(t, `out = int(undefined, 1)`, 1)
|
||||
expect(t, `out = int(undefined, 1.8)`, 1.8)
|
||||
expect(t, `out = int(undefined, string(1))`, "1")
|
||||
expect(t, `out = int(undefined, undefined)`, undefined())
|
||||
expect(t, `out = int(undefined, undefined)`, objects.UndefinedValue)
|
||||
|
||||
expect(t, `out = string(1)`, "1")
|
||||
expect(t, `out = string(1.8)`, "1.8")
|
||||
|
@ -41,40 +43,40 @@ func TestBuiltinFunction(t *testing.T) {
|
|||
expect(t, `out = string('8')`, "8")
|
||||
expect(t, `out = string([1,8.1,true,3])`, "[1, 8.1, true, 3]")
|
||||
expect(t, `out = string({b: "foo"})`, `{b: "foo"}`)
|
||||
expect(t, `out = string(undefined)`, undefined()) // not "undefined"
|
||||
expect(t, `out = string(undefined)`, objects.UndefinedValue) // not "undefined"
|
||||
expect(t, `out = string(1, "-522")`, "1")
|
||||
expect(t, `out = string(undefined, "-522")`, "-522") // not "undefined"
|
||||
|
||||
expect(t, `out = float(1)`, 1.0)
|
||||
expect(t, `out = float(1.8)`, 1.8)
|
||||
expect(t, `out = float("-52.2")`, -52.2)
|
||||
expect(t, `out = float(true)`, undefined())
|
||||
expect(t, `out = float(false)`, undefined())
|
||||
expect(t, `out = float('8')`, undefined())
|
||||
expect(t, `out = float([1,8.1,true,3])`, undefined())
|
||||
expect(t, `out = float({a: 1, b: "foo"})`, undefined())
|
||||
expect(t, `out = float(undefined)`, undefined())
|
||||
expect(t, `out = float(true)`, objects.UndefinedValue)
|
||||
expect(t, `out = float(false)`, objects.UndefinedValue)
|
||||
expect(t, `out = float('8')`, objects.UndefinedValue)
|
||||
expect(t, `out = float([1,8.1,true,3])`, objects.UndefinedValue)
|
||||
expect(t, `out = float({a: 1, b: "foo"})`, objects.UndefinedValue)
|
||||
expect(t, `out = float(undefined)`, objects.UndefinedValue)
|
||||
expect(t, `out = float("-52.2", 1.8)`, -52.2)
|
||||
expect(t, `out = float(undefined, 1)`, 1)
|
||||
expect(t, `out = float(undefined, 1.8)`, 1.8)
|
||||
expect(t, `out = float(undefined, "-52.2")`, "-52.2")
|
||||
expect(t, `out = float(undefined, char(56))`, '8')
|
||||
expect(t, `out = float(undefined, undefined)`, undefined())
|
||||
expect(t, `out = float(undefined, undefined)`, objects.UndefinedValue)
|
||||
|
||||
expect(t, `out = char(56)`, '8')
|
||||
expect(t, `out = char(1.8)`, undefined())
|
||||
expect(t, `out = char("-52.2")`, undefined())
|
||||
expect(t, `out = char(true)`, undefined())
|
||||
expect(t, `out = char(false)`, undefined())
|
||||
expect(t, `out = char(1.8)`, objects.UndefinedValue)
|
||||
expect(t, `out = char("-52.2")`, objects.UndefinedValue)
|
||||
expect(t, `out = char(true)`, objects.UndefinedValue)
|
||||
expect(t, `out = char(false)`, objects.UndefinedValue)
|
||||
expect(t, `out = char('8')`, '8')
|
||||
expect(t, `out = char([1,8.1,true,3])`, undefined())
|
||||
expect(t, `out = char({a: 1, b: "foo"})`, undefined())
|
||||
expect(t, `out = char(undefined)`, undefined())
|
||||
expect(t, `out = char([1,8.1,true,3])`, objects.UndefinedValue)
|
||||
expect(t, `out = char({a: 1, b: "foo"})`, objects.UndefinedValue)
|
||||
expect(t, `out = char(undefined)`, objects.UndefinedValue)
|
||||
expect(t, `out = char(56, 'a')`, '8')
|
||||
expect(t, `out = char(undefined, '8')`, '8')
|
||||
expect(t, `out = char(undefined, 56)`, 56)
|
||||
expect(t, `out = char(undefined, "-52.2")`, "-52.2")
|
||||
expect(t, `out = char(undefined, undefined)`, undefined())
|
||||
expect(t, `out = char(undefined, undefined)`, objects.UndefinedValue)
|
||||
|
||||
expect(t, `out = bool(1)`, true) // non-zero integer: true
|
||||
expect(t, `out = bool(0)`, false) // zero: true
|
||||
|
@ -93,20 +95,20 @@ func TestBuiltinFunction(t *testing.T) {
|
|||
expect(t, `out = bool(undefined)`, false) // undefined: false
|
||||
|
||||
expect(t, `out = bytes(1)`, []byte{0})
|
||||
expect(t, `out = bytes(1.8)`, undefined())
|
||||
expect(t, `out = bytes(1.8)`, objects.UndefinedValue)
|
||||
expect(t, `out = bytes("-522")`, []byte{'-', '5', '2', '2'})
|
||||
expect(t, `out = bytes(true)`, undefined())
|
||||
expect(t, `out = bytes(false)`, undefined())
|
||||
expect(t, `out = bytes('8')`, undefined())
|
||||
expect(t, `out = bytes([1])`, undefined())
|
||||
expect(t, `out = bytes({a: 1})`, undefined())
|
||||
expect(t, `out = bytes(undefined)`, undefined())
|
||||
expect(t, `out = bytes(true)`, objects.UndefinedValue)
|
||||
expect(t, `out = bytes(false)`, objects.UndefinedValue)
|
||||
expect(t, `out = bytes('8')`, objects.UndefinedValue)
|
||||
expect(t, `out = bytes([1])`, objects.UndefinedValue)
|
||||
expect(t, `out = bytes({a: 1})`, objects.UndefinedValue)
|
||||
expect(t, `out = bytes(undefined)`, objects.UndefinedValue)
|
||||
expect(t, `out = bytes("-522", ['8'])`, []byte{'-', '5', '2', '2'})
|
||||
expect(t, `out = bytes(undefined, "-522")`, "-522")
|
||||
expect(t, `out = bytes(undefined, 1)`, 1)
|
||||
expect(t, `out = bytes(undefined, 1.8)`, 1.8)
|
||||
expect(t, `out = bytes(undefined, int("-522"))`, -522)
|
||||
expect(t, `out = bytes(undefined, undefined)`, undefined())
|
||||
expect(t, `out = bytes(undefined, undefined)`, objects.UndefinedValue)
|
||||
|
||||
expect(t, `out = is_error(error(1))`, true)
|
||||
expect(t, `out = is_error(1)`, false)
|
||||
|
|
|
@ -2,13 +2,15 @@ package runtime_test
|
|||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func TestFunction(t *testing.T) {
|
||||
// function with no "return" statement returns "invalid" value.
|
||||
expect(t, `f1 := func() {}; out = f1();`, undefined())
|
||||
expect(t, `f1 := func() {}; f2 := func() { return f1(); }; f1(); out = f2();`, undefined())
|
||||
expect(t, `f := func(x) { x; }; out = f(5);`, undefined())
|
||||
expect(t, `f1 := func() {}; out = f1();`, objects.UndefinedValue)
|
||||
expect(t, `f1 := func() {}; f2 := func() { return f1(); }; f1(); out = f2();`, objects.UndefinedValue)
|
||||
expect(t, `f := func(x) { x; }; out = f(5);`, objects.UndefinedValue)
|
||||
|
||||
expect(t, `f := func(x) { return x; }; out = f(5);`, 5)
|
||||
expect(t, `f := func(x) { return x * 2; }; out = f(5);`, 10)
|
||||
|
|
|
@ -2,16 +2,18 @@ package runtime_test
|
|||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func TestIf(t *testing.T) {
|
||||
expect(t, `if (true) { out = 10 }`, 10)
|
||||
expect(t, `if (false) { out = 10 }`, undefined())
|
||||
expect(t, `if (false) { out = 10 }`, objects.UndefinedValue)
|
||||
expect(t, `if (false) { out = 10 } else { out = 20 }`, 20)
|
||||
expect(t, `if (1) { out = 10 }`, 10)
|
||||
expect(t, `if (0) { out = 10 } else { out = 20 }`, 20)
|
||||
expect(t, `if (1 < 2) { out = 10 }`, 10)
|
||||
expect(t, `if (1 > 2) { out = 10 }`, undefined())
|
||||
expect(t, `if (1 > 2) { out = 10 }`, objects.UndefinedValue)
|
||||
expect(t, `if (1 < 2) { out = 10 } else { out = 20 }`, 10)
|
||||
expect(t, `if (1 > 2) { out = 10 } else { out = 20 }`, 20)
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ func TestIndexable(t *testing.T) {
|
|||
dict := func() *StringDict { return &StringDict{Value: map[string]string{"a": "foo", "b": "bar"}} }
|
||||
expectWithSymbols(t, `out = dict["a"]`, "foo", SYM{"dict": dict()})
|
||||
expectWithSymbols(t, `out = dict["B"]`, "bar", SYM{"dict": dict()})
|
||||
expectWithSymbols(t, `out = dict["x"]`, undefined(), SYM{"dict": dict()})
|
||||
expectWithSymbols(t, `out = dict["x"]`, objects.UndefinedValue, SYM{"dict": dict()})
|
||||
expectErrorWithSymbols(t, `out = dict[0]`, SYM{"dict": dict()})
|
||||
|
||||
strCir := func() *StringCircle { return &StringCircle{Value: []string{"one", "two", "three"}} }
|
||||
|
@ -173,7 +173,7 @@ func TestIndexable(t *testing.T) {
|
|||
strArr := func() *StringArray { return &StringArray{Value: []string{"one", "two", "three"}} }
|
||||
expectWithSymbols(t, `out = arr["one"]`, 0, SYM{"arr": strArr()})
|
||||
expectWithSymbols(t, `out = arr["three"]`, 2, SYM{"arr": strArr()})
|
||||
expectWithSymbols(t, `out = arr["four"]`, undefined(), SYM{"arr": strArr()})
|
||||
expectWithSymbols(t, `out = arr["four"]`, objects.UndefinedValue, SYM{"arr": strArr()})
|
||||
expectWithSymbols(t, `out = arr[0]`, "one", SYM{"arr": strArr()})
|
||||
expectWithSymbols(t, `out = arr[1]`, "two", SYM{"arr": strArr()})
|
||||
expectErrorWithSymbols(t, `out = arr[-1]`, SYM{"arr": strArr()})
|
||||
|
|
|
@ -2,6 +2,8 @@ package runtime_test
|
|||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
|
@ -17,9 +19,9 @@ out = {
|
|||
})
|
||||
|
||||
expect(t, `out = {foo: 5}["foo"]`, 5)
|
||||
expect(t, `out = {foo: 5}["bar"]`, undefined())
|
||||
expect(t, `out = {foo: 5}["bar"]`, objects.UndefinedValue)
|
||||
expect(t, `key := "foo"; out = {foo: 5}[key]`, 5)
|
||||
expect(t, `out = {}["foo"]`, undefined())
|
||||
expect(t, `out = {}["foo"]`, objects.UndefinedValue)
|
||||
|
||||
expect(t, `
|
||||
m := {
|
||||
|
|
|
@ -47,17 +47,11 @@ os.remove("./temp")
|
|||
|
||||
// exec.command
|
||||
expect(t, `
|
||||
exec := import("exec")
|
||||
|
||||
echo := func(args) {
|
||||
cmd := exec.command("echo", args)
|
||||
if is_error(cmd) { return cmd.value }
|
||||
output := cmd.output()
|
||||
if is_error(output) { return output.value }
|
||||
return output
|
||||
os := import("os")
|
||||
cmd := os.exec("echo", "foo", "bar")
|
||||
if !is_error(cmd) {
|
||||
out = cmd.output()
|
||||
}
|
||||
|
||||
out = echo(["foo", "bar"])
|
||||
`, []byte("foo bar\n"))
|
||||
|
||||
// user modules
|
||||
|
|
|
@ -2,12 +2,14 @@ package runtime_test
|
|||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func TestSelector(t *testing.T) {
|
||||
expect(t, `a := {k1: 5, k2: "foo"}; out = a.k1`, 5)
|
||||
expect(t, `a := {k1: 5, k2: "foo"}; out = a.k2`, "foo")
|
||||
expect(t, `a := {k1: 5, k2: "foo"}; out = a.k3`, undefined())
|
||||
expect(t, `a := {k1: 5, k2: "foo"}; out = a.k3`, objects.UndefinedValue)
|
||||
|
||||
expect(t, `
|
||||
a := {
|
||||
|
|
|
@ -133,7 +133,10 @@ func toObject(v interface{}) objects.Object {
|
|||
case int: // for convenience
|
||||
return &objects.Int{Value: int64(v)}
|
||||
case bool:
|
||||
return &objects.Bool{Value: v}
|
||||
if v {
|
||||
return objects.TrueValue
|
||||
}
|
||||
return objects.FalseValue
|
||||
case rune:
|
||||
return &objects.Char{Value: v}
|
||||
case byte: // for convenience
|
||||
|
@ -326,7 +329,7 @@ func objectZeroCopy(o objects.Object) objects.Object {
|
|||
case *objects.Map:
|
||||
return &objects.Map{}
|
||||
case *objects.Undefined:
|
||||
return &objects.Undefined{}
|
||||
return objects.UndefinedValue
|
||||
case *objects.Error:
|
||||
return &objects.Error{}
|
||||
case *objects.Bytes:
|
||||
|
@ -341,7 +344,3 @@ func objectZeroCopy(o objects.Object) objects.Object {
|
|||
panic(fmt.Errorf("unknown object type: %s", o.TypeName()))
|
||||
}
|
||||
}
|
||||
|
||||
func undefined() *objects.Undefined {
|
||||
return &objects.Undefined{}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
package runtime_test
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func TestUndefined(t *testing.T) {
|
||||
expect(t, `out = undefined`, undefined())
|
||||
expect(t, `out = undefined`, objects.UndefinedValue)
|
||||
expect(t, `out = undefined == undefined`, true)
|
||||
expect(t, `out = undefined == 1`, false)
|
||||
expect(t, `out = 1 == undefined`, false)
|
||||
|
|
|
@ -8,8 +8,6 @@ import (
|
|||
"github.com/d5/tengo/runtime"
|
||||
)
|
||||
|
||||
var undefined objects.Object = &objects.Undefined{}
|
||||
|
||||
// Compiled is a compiled instance of the user script.
|
||||
// Use Script.Compile() to create Compiled object.
|
||||
type Compiled struct {
|
||||
|
@ -53,20 +51,18 @@ func (c *Compiled) IsDefined(name string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
_, isUndefined := (*v).(*objects.Undefined)
|
||||
|
||||
return !isUndefined
|
||||
return *v != objects.UndefinedValue
|
||||
}
|
||||
|
||||
// Get returns a variable identified by the name.
|
||||
func (c *Compiled) Get(name string) *Variable {
|
||||
value := &undefined
|
||||
value := &objects.UndefinedValue
|
||||
|
||||
symbol, _, ok := c.symbolTable.Resolve(name)
|
||||
if ok && symbol.Scope == compiler.ScopeGlobal {
|
||||
value = c.machine.Globals()[symbol.Index]
|
||||
if value == nil {
|
||||
value = &undefined
|
||||
value = &objects.UndefinedValue
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,7 +80,7 @@ func (c *Compiled) GetAll() []*Variable {
|
|||
if ok && symbol.Scope == compiler.ScopeGlobal {
|
||||
value := c.machine.Globals()[symbol.Index]
|
||||
if value == nil {
|
||||
value = &undefined
|
||||
value = &objects.UndefinedValue
|
||||
}
|
||||
|
||||
vars = append(vars, &Variable{
|
||||
|
|
|
@ -15,7 +15,10 @@ func objectToInterface(o objects.Object) interface{} {
|
|||
case *objects.Float:
|
||||
return val.Value
|
||||
case *objects.Bool:
|
||||
return val.Value
|
||||
if val == objects.TrueValue {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
case *objects.Char:
|
||||
return val.Value
|
||||
case *objects.String:
|
||||
|
|
|
@ -145,7 +145,5 @@ func (v *Variable) Object() objects.Object {
|
|||
|
||||
// IsUndefined returns true if the underlying value is undefined.
|
||||
func (v *Variable) IsUndefined() bool {
|
||||
_, isUndefined := (*v.value).(*objects.Undefined)
|
||||
|
||||
return isUndefined
|
||||
return *v.value == objects.UndefinedValue
|
||||
}
|
||||
|
|
|
@ -54,13 +54,12 @@ func TestVariable(t *testing.T) {
|
|||
FloatValue: 0,
|
||||
BoolValue: true,
|
||||
StringValue: "true",
|
||||
Object: &objects.Bool{Value: true},
|
||||
Object: objects.TrueValue,
|
||||
},
|
||||
{
|
||||
Name: "d",
|
||||
Value: nil,
|
||||
ValueType: "undefined",
|
||||
StringValue: "",
|
||||
Object: objects.UndefinedValue,
|
||||
IsUndefined: true,
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue