diff --git a/examples/point/point.q b/examples/point/point.q index 8c90e1f..2898895 100644 --- a/examples/point/point.q +++ b/examples/point/point.q @@ -1,3 +1,6 @@ +import mem +import sys + struct Point { x Int y Int @@ -5,7 +8,20 @@ struct Point { main() { p := new(Point) - p.x = 4 + p.x = 1 p.y = 2 + + out := mem.alloc(8) + out[0] = 'x' + out[1] = ' ' + out[2] = '0' + p.x + out[3] = '\n' + out[4] = 'y' + out[5] = ' ' + out[6] = '0' + p.y + out[7] = '\n' + sys.write(1, out, 8) + mem.free(out) + delete(p) } \ No newline at end of file diff --git a/src/core/CompileAssignField.go b/src/core/CompileAssignField.go index 4b74e78..26d462f 100644 --- a/src/core/CompileAssignField.go +++ b/src/core/CompileAssignField.go @@ -24,21 +24,19 @@ func (f *Function) CompileAssignField(node *ast.Assign) error { defer f.UseVariable(variable) pointer := variable.Type.(*types.Pointer) structure := pointer.To.(*types.Struct) + field := structure.FieldByName(fieldName) - for _, field := range structure.Fields { - if field.Name == fieldName { - memory := asm.Memory{ - Base: variable.Register, - Offset: field.Offset, - OffsetRegister: math.MaxUint8, - Length: field.Type.TotalSize(), - } - - _, err := f.ExpressionToMemory(value, memory) - return err - } + if field == nil { + return errors.New(&errors.UnknownStructField{StructName: structure.Name, FieldName: fieldName}, f.File, destination.Children[1].Token.Position) } - // TODO: unknown field error - return nil + memory := asm.Memory{ + Base: variable.Register, + Offset: field.Offset, + OffsetRegister: math.MaxUint8, + Length: field.Type.TotalSize(), + } + + _, err := f.ExpressionToMemory(value, memory) + return err } diff --git a/src/core/ExpressionToRegister.go b/src/core/ExpressionToRegister.go index 16d8e7c..684ad38 100644 --- a/src/core/ExpressionToRegister.go +++ b/src/core/ExpressionToRegister.go @@ -44,6 +44,17 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp return types.Int, err } + if node.Token.Kind == token.Period { + left := node.Children[0] + name := left.Token.Text(f.File.Bytes) + variable := f.VariableByName(name) + right := node.Children[1] + name = right.Token.Text(f.File.Bytes) + field := variable.Type.(*types.Pointer).To.(*types.Struct).FieldByName(name) + f.MemoryRegister(asm.LOAD, asm.Memory{Base: variable.Register, Offset: field.Offset, Length: field.Type.TotalSize()}, register) + return field.Type, nil + } + if len(node.Children) == 1 { if !node.Token.IsUnaryOperator() { return nil, errors.New(errors.MissingOperand, f.File, node.Token.End()) diff --git a/src/errors/UnknownStructField.go b/src/errors/UnknownStructField.go new file mode 100644 index 0000000..163d15c --- /dev/null +++ b/src/errors/UnknownStructField.go @@ -0,0 +1,19 @@ +package errors + +import "fmt" + +// UnknownStructField represents unknown struct fields. +type UnknownStructField struct { + StructName string + FieldName string + CorrectFieldName string +} + +// Error generates the string representation. +func (err *UnknownStructField) Error() string { + if err.CorrectFieldName != "" { + return fmt.Sprintf("Unknown struct field '%s' in '%s', did you mean '%s'?", err.FieldName, err.StructName, err.CorrectFieldName) + } + + return fmt.Sprintf("Unknown struct field '%s' in '%s'", err.FieldName, err.StructName) +} diff --git a/src/types/Struct.go b/src/types/Struct.go index d6f7c7a..d24bdb5 100644 --- a/src/types/Struct.go +++ b/src/types/Struct.go @@ -14,3 +14,13 @@ func (s *Struct) UniqueName() string { func (s *Struct) TotalSize() uint8 { return s.Size } + +func (s *Struct) FieldByName(name string) *Field { + for _, field := range s.Fields { + if field.Name == name { + return field + } + } + + return nil +} diff --git a/tests/errors/UnknownStructField.q b/tests/errors/UnknownStructField.q new file mode 100644 index 0000000..bea43e5 --- /dev/null +++ b/tests/errors/UnknownStructField.q @@ -0,0 +1,6 @@ +struct A {} + +main() { + a := new(A) + a.x = 1 +} \ No newline at end of file diff --git a/tests/errors_test.go b/tests/errors_test.go index 7839f63..7afb488 100644 --- a/tests/errors_test.go +++ b/tests/errors_test.go @@ -48,6 +48,7 @@ var errs = []struct { {"UnknownIdentifier2.q", &errors.UnknownIdentifier{Name: "x"}}, {"UnknownIdentifier3.q", &errors.UnknownIdentifier{Name: "x"}}, {"UnknownPackage.q", &errors.UnknownPackage{Name: "sys"}}, + {"UnknownStructField.q", &errors.UnknownStructField{StructName: "A", FieldName: "x"}}, {"UnusedImport.q", &errors.UnusedImport{Package: "sys"}}, {"UnusedVariable.q", &errors.UnusedVariable{Name: "x"}}, {"VariableAlreadyExists.q", &errors.VariableAlreadyExists{Name: "x"}}, diff --git a/tests/examples_test.go b/tests/examples_test.go index f889c1d..7373867 100644 --- a/tests/examples_test.go +++ b/tests/examples_test.go @@ -24,6 +24,7 @@ var examples = []struct { {"itoa", "", "9223372036854775807", 0}, {"collatz", "", "6 3 10 5 16 8 4 2 1", 0}, {"fizzbuzz", "", "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz", 0}, + {"point", "", "x 1\ny 2\n", 0}, {"prime", "", "2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97", 0}, } diff --git a/tests/programs/struct.q b/tests/programs/struct.q new file mode 100644 index 0000000..514a863 --- /dev/null +++ b/tests/programs/struct.q @@ -0,0 +1,34 @@ +struct Point { + x Int + y Int +} + +main() { + p := new(Point) + assert p.x == 0 + assert p.y == 0 + assert p.x == p.y + + p.x = 1 + p.y = 2 + assert p.x == 1 + assert p.y == 2 + assert p.x != p.y + + p.x = p.y + assert p.x == 2 + assert p.y == 2 + assert p.x == p.y + + p.x = p.y + 1 + assert p.x == 3 + assert p.y == 2 + assert p.x != p.y + + p.y = p.x + assert p.x == 3 + assert p.y == 3 + assert p.x == p.y + + delete(p) +} \ No newline at end of file diff --git a/tests/programs_test.go b/tests/programs_test.go index 8a6bbb3..f4b7d4d 100644 --- a/tests/programs_test.go +++ b/tests/programs_test.go @@ -60,6 +60,7 @@ var programs = []struct { {"loop", "", "", 0}, {"loop-lifetime", "", "", 0}, {"out-of-memory", "", "", 0}, + {"struct", "", "", 0}, } func TestPrograms(t *testing.T) {