Skip to content

Commit 03b7e50

Browse files
committed
Implement Range creation with step.
1 parent 9036b8c commit 03b7e50

6 files changed

Lines changed: 193 additions & 45 deletions

File tree

runtime/interpreter/interpreter_expression.go

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -553,39 +553,6 @@ func (interpreter *Interpreter) testComparison(left, right Value, expression *as
553553
}
554554
}
555555

556-
func (interpreter *Interpreter) createRange(left, right IntegerValue, expression *ast.BinaryExpression) *RangeValue {
557-
locationRange := LocationRange{
558-
Location: interpreter.Location,
559-
HasPosition: expression,
560-
}
561-
562-
leftComparable, leftOk := left.(ComparableValue)
563-
rightComparable, rightOk := right.(ComparableValue)
564-
565-
if !leftOk || !rightOk {
566-
panic(errors.NewUnreachableError())
567-
}
568-
569-
leftStaticType := left.StaticType(interpreter)
570-
rightStaticType := right.StaticType(interpreter)
571-
if leftStaticType != rightStaticType {
572-
// Checker would only allow same type on both sides of the create range expression.
573-
panic(errors.NewUnreachableError())
574-
}
575-
576-
if leftComparable.Greater(interpreter, rightComparable, locationRange) {
577-
panic(InvalidOperandsError{
578-
Operation: expression.Operation,
579-
LeftType: leftStaticType,
580-
RightType: rightStaticType,
581-
LocationRange: locationRange,
582-
})
583-
}
584-
585-
rangeStaticType := RangeStaticType{ElementType: leftStaticType}
586-
return NewRangeValue(interpreter, locationRange, left, right, rangeStaticType)
587-
}
588-
589556
func (interpreter *Interpreter) VisitUnaryExpression(expression *ast.UnaryExpression) Value {
590557
value := interpreter.evalExpression(expression.Expression)
591558

runtime/interpreter/value.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17817,6 +17817,7 @@ type RangeValue struct {
1781717817
semaType *sema.RangeType
1781817818
start IntegerValue
1781917819
endInclusive IntegerValue
17820+
step IntegerValue
1782017821
}
1782117822

1782217823
func NewRangeValue(
@@ -17825,10 +17826,38 @@ func NewRangeValue(
1782517826
start IntegerValue,
1782617827
endInclusive IntegerValue,
1782717828
rangeType RangeStaticType,
17829+
) *RangeValue {
17830+
startComparable, startOk := start.(ComparableValue)
17831+
endInclusiveComparable, endInclusiveOk := endInclusive.(ComparableValue)
17832+
if !startOk || !endInclusiveOk {
17833+
panic(errors.NewUnreachableError())
17834+
}
17835+
17836+
if startComparable.GreaterEqual(interpreter, endInclusiveComparable, locationRange) {
17837+
// stepValue should be 1
17838+
} else {
17839+
// stepValue should be -1
17840+
}
17841+
17842+
return &RangeValue{
17843+
start: start,
17844+
endInclusive: endInclusive,
17845+
Type: rangeType,
17846+
}
17847+
}
17848+
17849+
func NewRangeValueWithStep(
17850+
interpreter *Interpreter,
17851+
locationRange LocationRange,
17852+
start IntegerValue,
17853+
endInclusive IntegerValue,
17854+
step IntegerValue,
17855+
rangeType RangeStaticType,
1782817856
) *RangeValue {
1782917857
return &RangeValue{
1783017858
start: start,
1783117859
endInclusive: endInclusive,
17860+
step: step,
1783217861
Type: rangeType,
1783317862
}
1783417863
}

runtime/stdlib/builtin.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func DefaultStandardLibraryValues(handler StandardLibraryHandler) []StandardLibr
3939
PanicFunction,
4040
SignatureAlgorithmConstructor,
4141
RLPContract,
42+
RangeConstructorFunction,
4243
NewLogFunction(handler),
4344
NewUnsafeRandomFunction(handler),
4445
NewGetBlockFunction(handler),

runtime/stdlib/range.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,35 @@ var RangeConstructorFunction = NewStandardLibraryFunction(
8181
rangeConstructorFunctionType,
8282
rangeConstructorFunctionDocString,
8383
func(invocation interpreter.Invocation) interpreter.Value {
84-
panic("TODO")
84+
start, startOk := invocation.Arguments[0].(interpreter.IntegerValue)
85+
endInclusive, endInclusiveOk := invocation.Arguments[1].(interpreter.IntegerValue)
86+
87+
if !startOk || !endInclusiveOk {
88+
panic(errors.NewUnreachableError())
89+
}
90+
91+
inter := invocation.Interpreter
92+
locationRange := invocation.LocationRange
93+
94+
leftStaticType := start.StaticType(inter)
95+
rightStaticType := endInclusive.StaticType(inter)
96+
if leftStaticType != rightStaticType {
97+
// Checker would only allow same type for both start & endInclusive.
98+
panic(errors.NewUnreachableError())
99+
}
100+
101+
rangeStaticType := interpreter.RangeStaticType{ElementType: leftStaticType}
102+
103+
if len(invocation.Arguments) > 2 {
104+
step, ok := invocation.Arguments[2].(interpreter.IntegerValue)
105+
if !ok {
106+
panic(errors.NewUnreachableError())
107+
}
108+
109+
return interpreter.NewRangeValueWithStep(inter, locationRange, start, endInclusive, step, rangeStaticType)
110+
} else {
111+
return interpreter.NewRangeValue(inter, locationRange, start, endInclusive, rangeStaticType)
112+
}
85113
},
86114
)
87115

runtime/tests/checker/range_value_test.go

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,77 @@
1919
package checker
2020

2121
import (
22+
"fmt"
2223
"testing"
2324

25+
"github.com/onflow/cadence/runtime/sema"
26+
"github.com/onflow/cadence/runtime/stdlib"
2427
"github.com/stretchr/testify/require"
2528
)
2629

27-
func TestRangeDeclaration(t *testing.T) {
28-
30+
func TestRange(t *testing.T) {
2931
t.Parallel()
3032

31-
_, err := ParseAndCheck(t, `
32-
let a = 1 .. 10
33-
`)
33+
baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation)
34+
baseValueActivation.DeclareValue(stdlib.RangeConstructorFunction)
35+
36+
runValidCase := func(t *testing.T, memberType sema.Type, withStep bool) {
37+
t.Run(memberType.String(), func(t *testing.T) {
38+
t.Parallel()
39+
40+
var code string
41+
if withStep {
42+
code = fmt.Sprintf(
43+
`
44+
let s : %s = 10
45+
let e : %s = 20
46+
let step : %s = 2
47+
let r = Range(s, e, step: step)
48+
`,
49+
memberType.String(), memberType.String(), memberType.String())
50+
} else {
51+
code = fmt.Sprintf(
52+
`
53+
let s : %s = 10
54+
let e : %s = 20
55+
let r = Range(s, e)
56+
`,
57+
memberType.String(), memberType.String())
58+
}
59+
60+
checker, err := ParseAndCheckWithOptions(t, code,
61+
ParseAndCheckOptions{
62+
Config: &sema.Config{
63+
BaseValueActivation: baseValueActivation,
64+
},
65+
},
66+
)
67+
68+
require.NoError(t, err)
69+
resType := RequireGlobalValue(t, checker.Elaboration, "r")
70+
require.Equal(t,
71+
&sema.RangeType{
72+
MemberType: memberType,
73+
},
74+
resType,
75+
)
76+
})
77+
}
78+
79+
runValidCaseWithoutStep := func(t *testing.T, memberType sema.Type) {
80+
runValidCase(t, memberType, false)
81+
}
82+
runValidCaseWithStep := func(t *testing.T, memberType sema.Type) {
83+
runValidCase(t, memberType, true)
84+
}
85+
86+
for _, integerType := range sema.AllIntegerTypes {
87+
switch integerType {
88+
case sema.IntegerType, sema.SignedIntegerType:
89+
continue
90+
}
3491

35-
require.NoError(t, err)
92+
runValidCaseWithStep(t, integerType)
93+
runValidCaseWithoutStep(t, integerType)
94+
}
3695
}

runtime/tests/interpreter/range_value_test.go

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,78 @@
1919
package interpreter_test
2020

2121
import (
22+
"fmt"
2223
"testing"
23-
)
2424

25-
func TestRangeDeclaration(t *testing.T) {
25+
"github.com/onflow/cadence/runtime/activations"
26+
"github.com/onflow/cadence/runtime/interpreter"
27+
"github.com/onflow/cadence/runtime/sema"
28+
"github.com/onflow/cadence/runtime/stdlib"
29+
"github.com/stretchr/testify/require"
30+
)
2631

32+
func TestRange(t *testing.T) {
2733
t.Parallel()
2834

29-
_ = parseCheckAndInterpret(t, `
30-
let a = 1 .. 10
31-
`)
35+
baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation)
36+
baseValueActivation.DeclareValue(stdlib.RangeConstructorFunction)
37+
38+
baseActivation := activations.NewActivation(nil, interpreter.BaseActivation)
39+
interpreter.Declare(baseActivation, stdlib.RangeConstructorFunction)
40+
41+
runValidCase := func(t *testing.T, memberType sema.Type, withStep bool) {
42+
t.Run(memberType.String(), func(t *testing.T) {
43+
t.Parallel()
44+
45+
var code string
46+
if withStep {
47+
code = fmt.Sprintf(
48+
`
49+
let s : %s = 10
50+
let e : %s = 20
51+
let step : %s = 2
52+
let r = Range(s, e, step: step)
53+
`,
54+
memberType.String(), memberType.String(), memberType.String())
55+
} else {
56+
code = fmt.Sprintf(
57+
`
58+
let s : %s = 10
59+
let e : %s = 20
60+
let r = Range(s, e)
61+
`,
62+
memberType.String(), memberType.String())
63+
}
64+
65+
_, err := parseCheckAndInterpretWithOptions(t, code,
66+
ParseCheckAndInterpretOptions{
67+
CheckerConfig: &sema.Config{
68+
BaseValueActivation: baseValueActivation,
69+
},
70+
Config: &interpreter.Config{
71+
BaseActivation: baseActivation,
72+
},
73+
},
74+
)
75+
76+
require.NoError(t, err)
77+
})
78+
}
79+
80+
runValidCaseWithoutStep := func(t *testing.T, memberType sema.Type) {
81+
runValidCase(t, memberType, false)
82+
}
83+
runValidCaseWithStep := func(t *testing.T, memberType sema.Type) {
84+
runValidCase(t, memberType, true)
85+
}
86+
87+
for _, integerType := range sema.AllIntegerTypes {
88+
switch integerType {
89+
case sema.IntegerType, sema.SignedIntegerType:
90+
continue
91+
}
92+
93+
runValidCaseWithStep(t, integerType)
94+
runValidCaseWithoutStep(t, integerType)
95+
}
3296
}

0 commit comments

Comments
 (0)