Skip to content

Commit

Permalink
Implement Range creation with step.
Browse files Browse the repository at this point in the history
  • Loading branch information
darkdrag00nv2 committed Jun 9, 2023
1 parent b595d86 commit fd72061
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 45 deletions.
33 changes: 0 additions & 33 deletions runtime/interpreter/interpreter_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -553,39 +553,6 @@ func (interpreter *Interpreter) testComparison(left, right Value, expression *as
}
}

func (interpreter *Interpreter) createRange(left, right IntegerValue, expression *ast.BinaryExpression) *RangeValue {
locationRange := LocationRange{
Location: interpreter.Location,
HasPosition: expression,
}

leftComparable, leftOk := left.(ComparableValue)
rightComparable, rightOk := right.(ComparableValue)

if !leftOk || !rightOk {
panic(errors.NewUnreachableError())
}

leftStaticType := left.StaticType(interpreter)
rightStaticType := right.StaticType(interpreter)
if leftStaticType != rightStaticType {
// Checker would only allow same type on both sides of the create range expression.
panic(errors.NewUnreachableError())
}

if leftComparable.Greater(interpreter, rightComparable, locationRange) {
panic(InvalidOperandsError{
Operation: expression.Operation,
LeftType: leftStaticType,
RightType: rightStaticType,
LocationRange: locationRange,
})
}

rangeStaticType := RangeStaticType{ElementType: leftStaticType}
return NewRangeValue(interpreter, locationRange, left, right, rangeStaticType)
}

func (interpreter *Interpreter) VisitUnaryExpression(expression *ast.UnaryExpression) Value {
value := interpreter.evalExpression(expression.Expression)

Expand Down
29 changes: 29 additions & 0 deletions runtime/interpreter/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -17817,6 +17817,7 @@ type RangeValue struct {
semaType *sema.RangeType
start IntegerValue
endInclusive IntegerValue
step IntegerValue
}

func NewRangeValue(
Expand All @@ -17825,10 +17826,38 @@ func NewRangeValue(
start IntegerValue,
endInclusive IntegerValue,
rangeType RangeStaticType,
) *RangeValue {
startComparable, startOk := start.(ComparableValue)
endInclusiveComparable, endInclusiveOk := endInclusive.(ComparableValue)
if !startOk || !endInclusiveOk {
panic(errors.NewUnreachableError())
}

if startComparable.GreaterEqual(interpreter, endInclusiveComparable, locationRange) {
// stepValue should be 1
} else {
// stepValue should be -1
}

return &RangeValue{
start: start,
endInclusive: endInclusive,
Type: rangeType,
}
}

func NewRangeValueWithStep(
interpreter *Interpreter,
locationRange LocationRange,
start IntegerValue,
endInclusive IntegerValue,
step IntegerValue,
rangeType RangeStaticType,
) *RangeValue {
return &RangeValue{
start: start,
endInclusive: endInclusive,
step: step,
Type: rangeType,
}
}
Expand Down
1 change: 1 addition & 0 deletions runtime/stdlib/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func DefaultStandardLibraryValues(handler StandardLibraryHandler) []StandardLibr
PanicFunction,
SignatureAlgorithmConstructor,
RLPContract,
RangeConstructorFunction,
NewLogFunction(handler),
NewUnsafeRandomFunction(handler),
NewGetBlockFunction(handler),
Expand Down
30 changes: 29 additions & 1 deletion runtime/stdlib/range.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,35 @@ var RangeConstructorFunction = NewStandardLibraryFunction(
rangeConstructorFunctionType,
rangeConstructorFunctionDocString,
func(invocation interpreter.Invocation) interpreter.Value {
panic("TODO")
start, startOk := invocation.Arguments[0].(interpreter.IntegerValue)
endInclusive, endInclusiveOk := invocation.Arguments[1].(interpreter.IntegerValue)

if !startOk || !endInclusiveOk {
panic(errors.NewUnreachableError())
}

inter := invocation.Interpreter
locationRange := invocation.LocationRange

leftStaticType := start.StaticType(inter)
rightStaticType := endInclusive.StaticType(inter)
if leftStaticType != rightStaticType {
// Checker would only allow same type for both start & endInclusive.
panic(errors.NewUnreachableError())
}

rangeStaticType := interpreter.RangeStaticType{ElementType: leftStaticType}

if len(invocation.Arguments) > 2 {
step, ok := invocation.Arguments[2].(interpreter.IntegerValue)
if !ok {
panic(errors.NewUnreachableError())
}

return interpreter.NewRangeValueWithStep(inter, locationRange, start, endInclusive, step, rangeStaticType)
} else {
return interpreter.NewRangeValue(inter, locationRange, start, endInclusive, rangeStaticType)
}
},
)

Expand Down
71 changes: 65 additions & 6 deletions runtime/tests/checker/range_value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,77 @@
package checker

import (
"fmt"
"testing"

"github.com/onflow/cadence/runtime/sema"
"github.com/onflow/cadence/runtime/stdlib"
"github.com/stretchr/testify/require"
)

func TestRangeDeclaration(t *testing.T) {

func TestRange(t *testing.T) {
t.Parallel()

_, err := ParseAndCheck(t, `
let a = 1 .. 10
`)
baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation)
baseValueActivation.DeclareValue(stdlib.RangeConstructorFunction)

runValidCase := func(t *testing.T, memberType sema.Type, withStep bool) {
t.Run(memberType.String(), func(t *testing.T) {
t.Parallel()

var code string
if withStep {
code = fmt.Sprintf(
`
let s : %s = 10
let e : %s = 20
let step : %s = 2
let r = Range(s, e, step: step)
`,
memberType.String(), memberType.String(), memberType.String())
} else {
code = fmt.Sprintf(
`
let s : %s = 10
let e : %s = 20
let r = Range(s, e)
`,
memberType.String(), memberType.String())
}

checker, err := ParseAndCheckWithOptions(t, code,
ParseAndCheckOptions{
Config: &sema.Config{
BaseValueActivation: baseValueActivation,
},
},
)

require.NoError(t, err)
resType := RequireGlobalValue(t, checker.Elaboration, "r")
require.Equal(t,
&sema.RangeType{
MemberType: memberType,
},
resType,
)
})
}

runValidCaseWithoutStep := func(t *testing.T, memberType sema.Type) {
runValidCase(t, memberType, false)
}
runValidCaseWithStep := func(t *testing.T, memberType sema.Type) {
runValidCase(t, memberType, true)
}

for _, integerType := range sema.AllIntegerTypes {
switch integerType {
case sema.IntegerType, sema.SignedIntegerType:
continue
}

require.NoError(t, err)
runValidCaseWithStep(t, integerType)
runValidCaseWithoutStep(t, integerType)
}
}
74 changes: 69 additions & 5 deletions runtime/tests/interpreter/range_value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,78 @@
package interpreter_test

import (
"fmt"
"testing"
)

func TestRangeDeclaration(t *testing.T) {
"github.com/onflow/cadence/runtime/activations"
"github.com/onflow/cadence/runtime/interpreter"
"github.com/onflow/cadence/runtime/sema"
"github.com/onflow/cadence/runtime/stdlib"
"github.com/stretchr/testify/require"
)

func TestRange(t *testing.T) {
t.Parallel()

_ = parseCheckAndInterpret(t, `
let a = 1 .. 10
`)
baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation)
baseValueActivation.DeclareValue(stdlib.RangeConstructorFunction)

baseActivation := activations.NewActivation(nil, interpreter.BaseActivation)
interpreter.Declare(baseActivation, stdlib.RangeConstructorFunction)

runValidCase := func(t *testing.T, memberType sema.Type, withStep bool) {
t.Run(memberType.String(), func(t *testing.T) {
t.Parallel()

var code string
if withStep {
code = fmt.Sprintf(
`
let s : %s = 10
let e : %s = 20
let step : %s = 2
let r = Range(s, e, step: step)
`,
memberType.String(), memberType.String(), memberType.String())
} else {
code = fmt.Sprintf(
`
let s : %s = 10
let e : %s = 20
let r = Range(s, e)
`,
memberType.String(), memberType.String())
}

_, err := parseCheckAndInterpretWithOptions(t, code,
ParseCheckAndInterpretOptions{
CheckerConfig: &sema.Config{
BaseValueActivation: baseValueActivation,
},
Config: &interpreter.Config{
BaseActivation: baseActivation,
},
},
)

require.NoError(t, err)
})
}

runValidCaseWithoutStep := func(t *testing.T, memberType sema.Type) {
runValidCase(t, memberType, false)
}
runValidCaseWithStep := func(t *testing.T, memberType sema.Type) {
runValidCase(t, memberType, true)
}

for _, integerType := range sema.AllIntegerTypes {
switch integerType {
case sema.IntegerType, sema.SignedIntegerType:
continue
}

runValidCaseWithStep(t, integerType)
runValidCaseWithoutStep(t, integerType)
}
}

0 comments on commit fd72061

Please sign in to comment.