Skip to content

Commit 87f1e92

Browse files
committed
Implement struct assignment chaining (https://github.com/monstermichl/TypeShell/issues)
1 parent f38a35f commit 87f1e92

6 files changed

Lines changed: 91 additions & 64 deletions

File tree

parser/parser.go

Lines changed: 39 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2563,35 +2563,36 @@ func (p *Parser) evaluateTypeDefinition(ctx context) (Expression, error) {
25632563
}, nil
25642564
}
25652565

2566-
func (p *Parser) evaluateStructEvaluation(importAlias string, ctx context) (Expression, error) {
2566+
func (p *Parser) evaluateStructFields(importAlias string, stopOnLastStruct bool, ctx context) (Expression, StructField, error) {
25672567
identifierToken := p.peek() // Eat identifier token.
25682568

25692569
if identifierToken.Type() != lexer.IDENTIFIER {
2570-
return nil, p.expectedIdentifierError(identifierToken)
2570+
return nil, StructField{}, p.expectedIdentifierError(identifierToken)
25712571
}
25722572
value, err := p.evaluateNamedValueEvaluation(ctx)
25732573

25742574
if err != nil {
2575-
return nil, err
2575+
return nil, StructField{}, err
25762576
}
2577-
var expr Expression
2577+
expr := value
2578+
var structField StructField
25782579

25792580
for {
25802581
dotToken := p.eat()
25812582

25822583
if dotToken.Type() != lexer.DOT {
2583-
return nil, p.expectedError(`"."`, dotToken)
2584+
return nil, StructField{}, p.expectedError(`"."`, dotToken)
25842585
}
25852586
fieldToken := p.eat()
25862587

25872588
if fieldToken.Type() != lexer.IDENTIFIER {
2588-
return nil, p.expectedError("field name", fieldToken)
2589+
return nil, StructField{}, p.expectedError("field name", fieldToken)
25892590
}
25902591
typeDeclaration := value.ValueType().Type()
25912592
typeDeclarationKind := typeDeclaration.Kind()
25922593

25932594
if typeDeclarationKind != TypeKindStruct {
2594-
return nil, p.expectedError(fmt.Sprintf("%s but got %s", TypeKindStruct, typeDeclarationKind), identifierToken)
2595+
return nil, StructField{}, p.expectedError(fmt.Sprintf("%s but got %s", TypeKindStruct, typeDeclarationKind), identifierToken)
25952596
}
25962597
structDefinition := typeDeclaration.(StructDefinition)
25972598

@@ -2600,20 +2601,32 @@ func (p *Parser) evaluateStructEvaluation(importAlias string, ctx context) (Expr
26002601
foundField, err := structDefinition.FindField(fieldName)
26012602

26022603
if err != nil {
2603-
return nil, p.atError(err.Error(), fieldToken)
2604+
return nil, StructField{}, p.atError(err.Error(), fieldToken)
26042605
}
2605-
expr = StructEvaluation{
2606+
structField = foundField
2607+
exprTemp := StructEvaluation{
26062608
value: value,
26072609
field: foundField,
26082610
}
2609-
2611+
exprTempKind := exprTemp.ValueType().Type().Kind()
2612+
2613+
if exprTempKind != TypeKindStruct && stopOnLastStruct {
2614+
break
2615+
}
2616+
expr = exprTemp
2617+
26102618
// Allow chaining.
2611-
if p.peek().Type() != lexer.DOT || expr.ValueType().Type().Kind() != TypeKindStruct {
2619+
if p.peek().Type() != lexer.DOT || exprTempKind != TypeKindStruct {
26122620
break
26132621
}
26142622
value = expr
26152623
}
2616-
return expr, nil
2624+
return expr, structField, nil
2625+
}
2626+
2627+
func (p *Parser) evaluateStructEvaluation(importAlias string, ctx context) (Expression, error) {
2628+
expr, _, err := p.evaluateStructFields(importAlias, false, ctx)
2629+
return expr, err
26172630
}
26182631

26192632
func (p *Parser) evaluateNamedValueEvaluation(ctx context) (Expression, error) {
@@ -3554,65 +3567,33 @@ func (p *Parser) evaluateSliceAssignment(ctx context) (Statement, error) {
35543567
}
35553568

35563569
func (p *Parser) evaluateStructAssignment(ctx context) (Statement, error) {
3557-
nameToken := p.eat()
3558-
3559-
if nameToken.Type() != lexer.IDENTIFIER {
3560-
return nil, p.expectedError("struct variable", nameToken)
3561-
}
3562-
name := nameToken.Value()
3563-
namedValue, exists := ctx.findNamedValue(name, p.prefix, ctx.global())
3564-
3565-
if !exists {
3566-
return nil, p.variableNotDefinedError(name, nameToken)
3567-
} else if namedValue.IsConstant() {
3568-
return nil, p.constantError(name, nameToken)
3569-
}
3570-
namedValueValueType := namedValue.ValueType()
3571-
namedValueBaseType := namedValueValueType.Type()
3572-
3573-
if namedValueValueType.IsSlice() {
3574-
return nil, p.expectedError("struct but got slice", nameToken)
3575-
} else if namedValueBaseType.Kind() != TypeKindStruct {
3576-
return nil, p.expectedError(fmt.Sprintf("struct but variable is of type %s", namedValueBaseType.Kind()), nameToken)
3577-
}
3578-
structDefinition := namedValueBaseType.(StructDefinition)
3579-
nextToken := p.eat()
3580-
3581-
if nextToken.Type() != lexer.DOT {
3582-
return nil, p.expectedError(`"."`, nextToken)
3583-
}
3584-
nextToken = p.eat()
3585-
3586-
if nextToken.Type() != lexer.IDENTIFIER {
3587-
return nil, p.expectedError(`struct field`, nextToken)
3588-
}
3589-
structField, err := structDefinition.FindField(nextToken.Value())
3570+
expr, field, err := p.evaluateStructFields("", true, ctx) // TODO: Find out if importAlias must be passed correctly.
35903571

35913572
if err != nil {
3592-
return nil, p.atError(err.Error(), nextToken)
3573+
return nil, err
35933574
}
3594-
nextToken = p.eat()
3575+
nextToken := p.eat()
35953576

35963577
if nextToken.Type() != lexer.ASSIGN_OPERATOR {
3597-
return nil, p.expectedError(`"="`, nameToken)
3578+
return nil, p.expectedError(`"="`, nextToken)
35983579
}
3599-
valueToken := p.peek()
3600-
value, err := p.evaluateExpression(ctx)
3580+
assignedValueToken := p.peek()
3581+
assignedValue, err := p.evaluateExpression(ctx)
36013582

36023583
if err != nil {
36033584
return nil, err
36043585
}
3605-
variableValueType := structField.ValueType()
3606-
assignedValueType := value.ValueType()
3586+
fieldValueType := field.ValueType()
3587+
assignedValueType := assignedValue.ValueType()
36073588

3608-
if !variableValueType.Equals(assignedValueType) {
3609-
return nil, p.expectedError(fmt.Sprintf("%s value but got %s", variableValueType.String(), assignedValueType.String()), valueToken)
3589+
if !fieldValueType.Equals(assignedValueType) {
3590+
return nil, p.expectedError(fmt.Sprintf("%s value but got %s", fieldValueType.String(), assignedValueType.String()), assignedValueToken)
36103591
}
36113592
return StructAssignment{
3612-
Variable: namedValue.(Variable),
3613-
value: StructValue{
3614-
StructField: structField,
3615-
value: value,
3593+
value: expr,
3594+
assignment: StructValue{
3595+
StructField: field,
3596+
value: assignedValue,
36163597
},
36173598
}, nil
36183599
}

parser/struct.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,18 +130,30 @@ func (d StructInitialization) Values() []StructValue {
130130
}
131131

132132
type StructAssignment struct {
133-
Variable
134-
value StructValue
133+
value Expression
134+
assignment StructValue
135135
}
136136

137137
func (a StructAssignment) StatementType() StatementType {
138138
return STATEMENT_TYPE_STRUCT_ASSIGNMENT
139139
}
140140

141-
func (a StructAssignment) Value() StructValue {
141+
func (a StructAssignment) ValueType() ValueType {
142+
return a.assignment.ValueType()
143+
}
144+
145+
func (a StructAssignment) IsConstant() bool {
146+
return false
147+
}
148+
149+
func (a StructAssignment) Value() Expression {
142150
return a.value
143151
}
144152

153+
func (a StructAssignment) Assignment() StructValue {
154+
return a.assignment
155+
}
156+
145157
type StructEvaluation struct {
146158
value Expression
147159
field StructField

tests/struct.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,27 @@ func testStructEvaluationChainingSuccess(t *testing.T, transpilerFunc transpiler
182182
})
183183
}
184184

185+
func testStructAssignmentChainingSuccess(t *testing.T, transpilerFunc transpilerFunc) {
186+
transpilerFunc(t, `
187+
type myStruct struct {
188+
a string
189+
}
190+
191+
type myStruct2 struct {
192+
a string
193+
b myStruct
194+
}
195+
196+
s := myStruct2{a: "Hello", b: myStruct{a: "World"}}
197+
s.b.a = "Mars"
198+
199+
print(s.a, s.b.a)
200+
`, func(output string, err error) {
201+
require.Nil(t, err)
202+
require.Equal(t, "Hello Mars", output)
203+
})
204+
}
205+
185206
func testStructFieldAssignedTwiceInInitializationFail(t *testing.T, transpilerFunc transpilerFunc) {
186207
transpilerFunc(t, `
187208
type myStruct struct {

tests/struct_linux_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ func TestNestedStructSuccess(t *testing.T) {
3232
testNestedStructSuccess(t, transpileBash)
3333
}
3434

35+
func TestStructAssignmentChainingSuccess(t *testing.T) {
36+
testStructAssignmentChainingSuccess(t, transpileBash)
37+
}
38+
3539
func TestStructFieldAssignedTwiceInInitializationFail(t *testing.T) {
3640
testStructFieldAssignedTwiceInInitializationFail(t, transpileBash)
3741
}

tests/struct_windows_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ func TestStructEvaluationChainingSuccess(t *testing.T) {
3636
testStructEvaluationChainingSuccess(t, transpileBatch)
3737
}
3838

39+
func TestStructAssignmentChainingSuccess(t *testing.T) {
40+
testStructAssignmentChainingSuccess(t, transpileBatch)
41+
}
42+
3943
func TestStructFieldAssignedTwiceInInitializationFail(t *testing.T) {
4044
testStructFieldAssignedTwiceInInitializationFail(t, transpileBatch)
4145
}

transpiler/transpiler.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -561,8 +561,13 @@ func (t *transpiler) evaluateSliceAssignment(assignment parser.SliceAssignment)
561561
}
562562

563563
func (t *transpiler) evaluateStructAssignment(assignment parser.StructAssignment) error {
564-
value := assignment.Value()
565-
valueResult, err := t.evaluateExpressionAssignment(value.Value())
564+
structResult, err := t.evaluateExpression(assignment.Value(), true)
565+
566+
if err != nil {
567+
return err
568+
}
569+
fieldAssignment := assignment.Assignment()
570+
valueResult, err := t.evaluateExpressionAssignment(fieldAssignment.Value())
566571

567572
if err != nil {
568573
return err
@@ -571,7 +576,7 @@ func (t *transpiler) evaluateStructAssignment(assignment parser.StructAssignment
571576
if err != nil {
572577
return err
573578
}
574-
return t.converter.StructAssignment(assignment.LayerName(), value.Name(), valueResult.firstValue(), assignment.Global())
579+
return t.converter.StructAssignment(structResult.firstValue(), fieldAssignment.Name(), valueResult.firstValue(), false)
575580
}
576581

577582
func (t *transpiler) evaluateConstEvaluation(evaluation parser.ConstEvaluation, valueUsed bool) (expressionResult, error) {

0 commit comments

Comments
 (0)