Skip to content

Commit 4293e52

Browse files
authored
Save statement implementation (#17)
1 parent db8980f commit 4293e52

24 files changed

+1452
-386
lines changed

Numscript.g4

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ ALLOWING: 'allowing';
1919
UNBOUNDED: 'unbounded';
2020
OVERDRAFT: 'overdraft';
2121
KEPT: 'kept';
22+
SAVE: 'save';
2223
LPARENS: '(';
2324
RPARENS: ')';
2425
LBRACKET: '[';
@@ -100,4 +101,5 @@ sentValue: literal # sentLiteral | sentAllLit # sentAll;
100101
101102
statement:
102103
SEND sentValue LPARENS SOURCE EQ source DESTINATION EQ destination RPARENS # sendStatement
104+
| SAVE sentValue FROM literal # saveStatement
103105
| functionCall # fnCallStatement;

internal/analysis/check.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ func (res *CheckResult) checkStatement(statement parser.Statement) {
173173
res.emptiedAccount = make(map[string]struct{})
174174

175175
switch statement := statement.(type) {
176+
case *parser.SaveStatement:
177+
res.checkSentValue(statement.SentValue)
178+
res.checkLiteral(statement.Literal, TypeAccount)
179+
176180
case *parser.SendStatement:
177181
_, isSendAll := statement.SentValue.(*parser.SentValueAll)
178182
res.unboundedSend = isSendAll

internal/analysis/check_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,62 @@ func TestDuplicateVariable(t *testing.T) {
8585
)
8686
}
8787

88+
func TestUnboundVarInSaveAccount(t *testing.T) {
89+
t.Parallel()
90+
91+
input := `save $unbound_mon from $unbound_acc`
92+
93+
program := parser.Parse(input).Value
94+
95+
diagnostics := analysis.CheckProgram(program).Diagnostics
96+
require.Len(t, diagnostics, 2)
97+
98+
assert.Equal(t,
99+
[]analysis.Diagnostic{
100+
{
101+
Kind: &analysis.UnboundVariable{Name: "unbound_mon"},
102+
Range: parser.RangeOfIndexed(input, "$unbound_mon", 0),
103+
},
104+
{
105+
Kind: &analysis.UnboundVariable{Name: "unbound_acc"},
106+
Range: parser.RangeOfIndexed(input, "$unbound_acc", 0),
107+
},
108+
},
109+
diagnostics,
110+
)
111+
}
112+
113+
func TestMismatchedTypeInSave(t *testing.T) {
114+
t.Parallel()
115+
116+
input := `vars {
117+
string $str
118+
number $n
119+
}
120+
121+
save $str from $n
122+
`
123+
124+
program := parser.Parse(input).Value
125+
126+
diagnostics := analysis.CheckProgram(program).Diagnostics
127+
require.Len(t, diagnostics, 2)
128+
129+
assert.Equal(t,
130+
[]analysis.Diagnostic{
131+
{
132+
Kind: &analysis.TypeMismatch{Expected: "monetary", Got: "string"},
133+
Range: parser.RangeOfIndexed(input, "$str", 1),
134+
},
135+
{
136+
Kind: &analysis.TypeMismatch{Expected: "account", Got: "number"},
137+
Range: parser.RangeOfIndexed(input, "$n", 1),
138+
},
139+
},
140+
diagnostics,
141+
)
142+
}
143+
88144
func TestUnboundVarInSource(t *testing.T) {
89145
t.Parallel()
90146

internal/analysis/goto_definition_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/formancehq/numscript/internal/parser"
88

99
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
1011
)
1112

1213
func TestGotoDefinitionOnSendMonetaryVar(t *testing.T) {
@@ -23,7 +24,7 @@ send $amt (
2324
checkResult := analysis.CheckProgram(program)
2425

2526
res := analysis.GotoDefinition(program, rng.Start, checkResult)
26-
assert.NotNil(t, res)
27+
require.NotNil(t, res)
2728

2829
assert.Equal(t, &analysis.GotoDefinitionResult{
2930
Range: parser.RangeOfIndexed(input, "$amt", 0),

internal/analysis/hover.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ func HoverOn(program parser.Program, position parser.Position) Hover {
5050
return hover
5151
}
5252

53+
case *parser.SaveStatement:
54+
hover := hoverOnSaveStatement(*statement, position)
55+
if hover != nil {
56+
return hover
57+
}
58+
5359
case *parser.FnCall:
5460
hover := hoverOnFnCall(*statement, position)
5561
if hover != nil {
@@ -92,6 +98,24 @@ func hoverOnSentValue(sentValue parser.SentValue, position parser.Position) Hove
9298
}
9399
}
94100

101+
func hoverOnSaveStatement(saveStatement parser.SaveStatement, position parser.Position) Hover {
102+
if !saveStatement.Range.Contains(position) {
103+
return nil
104+
}
105+
106+
hover := hoverOnSentValue(saveStatement.SentValue, position)
107+
if hover != nil {
108+
return hover
109+
}
110+
111+
hover = hoverOnLiteral(saveStatement.Literal, position)
112+
if hover != nil {
113+
return hover
114+
}
115+
116+
return nil
117+
}
118+
95119
func hoverOnSendStatement(sendStatement parser.SendStatement, position parser.Position) Hover {
96120
if !sendStatement.Range.Contains(position) {
97121
return nil

0 commit comments

Comments
 (0)