Skip to content

Commit

Permalink
Showing 50 changed files with 3,147 additions and 671 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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

88 changes: 38 additions & 50 deletions assert/assert.go
Original file line number Diff line number Diff line change
@@ -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,59 +244,59 @@ 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
}
}

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
}
}

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
}
}
35 changes: 34 additions & 1 deletion compiler/bytecode.go
Original file line number Diff line number Diff line change
@@ -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{})
}
14 changes: 10 additions & 4 deletions compiler/bytecode_test.go
Original file line number Diff line number Diff line change
@@ -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(
Loading

0 comments on commit fdc52a0

Please sign in to comment.