From fc930fa3d4cea7c6ab4d95aa84ad8953b5ef5c49 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Thu, 11 Apr 2024 20:46:59 -0400 Subject: [PATCH] Added a Description() method to Expression --- analyzer/assignment.go | 55 ++---------------------------------------- entity/expression.go | 28 +++++++++++++++++++++ entity/literal.go | 7 ++++++ 3 files changed, 37 insertions(+), 53 deletions(-) diff --git a/analyzer/assignment.go b/analyzer/assignment.go index 94fda08..05fe2bc 100644 --- a/analyzer/assignment.go +++ b/analyzer/assignment.go @@ -369,70 +369,19 @@ func (this *Tree) areStructurallyEquivalent (left, right entity.Type) bool { // isLocationExpression returns whether or not an expression is a valid location // expression. func (this *Tree) isLocationExpression (expression entity.Expression) error { - // TODO: have some Name() method of Expression so we can use it in a - // default case here. this will prevent crashes when new features are - // added. - cannot := func (pos errors.Position, kind string) error { - return errors.Errorf(pos, "cannot assign to %s", kind) - } switch expression := expression.(type) { case *entity.Variable: return nil case *entity.Declaration: return nil - case *entity.Call: - return cannot(expression.Position(), "function call") - case *entity.MethodCall: - return cannot(expression.Position(), "method call") case *entity.Subscript: return this.isLocationExpression(expression.Slice) - case *entity.Slice: - return cannot(expression.Position(), "slice operation") case *entity.Dereference: return this.isLocationExpression(expression.Pointer) - case *entity.Reference: - return cannot(expression.Position(), "reference operation") - case *entity.ValueCast: - return cannot(expression.Position(), "value cast") - case *entity.BitCast: - return cannot(expression.Position(), "bit cast") - case *entity.Operation: - return cannot(expression.Position(), fmt.Sprintf ( - "cannot assign to %v operation", - expression.Operator)) - case *entity.Block: - return cannot(expression.Position(), "block") case *entity.MemberAccess: - return this.isLocationExpression ( - expression.Source) - case *entity.IfElse: - return cannot(expression.Position(), "if/else") - case *entity.Match: - return cannot(expression.Position(), "match") - case *entity.Loop: - return cannot(expression.Position(), "loop") - case *entity.Break: - return cannot(expression.Position(), "break statement") - case *entity.Return: - return cannot(expression.Position(), "return statement") - case *entity.LiteralInt: - return cannot(expression.Position(), "integer literal") - case *entity.LiteralFloat: - return cannot(expression.Position(), "float literal") - case *entity.LiteralString: - return cannot(expression.Position(), "string literal") - case *entity.LiteralArray: - return cannot(expression.Position(), "array literal") - case *entity.LiteralStruct: - return cannot(expression.Position(), "struct literal") - case *entity.LiteralBoolean: - return cannot(expression.Position(), "boolean literal") - case *entity.LiteralNil: - return cannot(expression.Position(), "nil literal") + return this.isLocationExpression(expression.Source) default: - panic(fmt.Sprint ( - "BUG: analyzer doesnt know about expression", - expression)) + return errors.Errorf(pos, "cannot assign to %s", expression.Description()) } } diff --git a/entity/expression.go b/entity/expression.go index bd75f04..f4c36a9 100644 --- a/entity/expression.go +++ b/entity/expression.go @@ -26,6 +26,9 @@ type Expression interface { // Constant returns true if the expression's value can be computed at // compile time. IsConstant () bool + + // Description returns a human-readable description of the expression. + Description () string expression () } @@ -48,6 +51,7 @@ func (this *Variable) Position () errors.Position { return this.Pos } func (this *Variable) Type () Type { return this.Declaration.Type() } func (this *Variable) HasExplicitType () bool { return true } func (this *Variable) IsConstant () bool { return false } +func (this *Variable) Description () string { return "variable" } func (this *Variable) String () string { return this.Name } @@ -68,6 +72,7 @@ func (this *Declaration) Position () errors.Position { return this.Pos } func (this *Declaration) Type () Type { return this.Ty } func (this *Declaration) HasExplicitType () bool { return true } func (this *Declaration) IsConstant () bool { return false } +func (this *Declaration) Description () string { return "declaration" } func (this *Declaration) String () string { return fmt.Sprint(this.Name, ":", this.Ty) } @@ -95,6 +100,7 @@ func (this *Call) Position () errors.Position { return this.Pos } func (this *Call) Type () Type { return this.Function.Signature.Return } func (this *Call) HasExplicitType () bool { return true } func (this *Call) IsConstant () bool { return false } +func (this *Call) Description () string { return "function call" } func (this *Call) String () string { out := "" if this.UnitNickname != "" { @@ -137,6 +143,7 @@ func (this *MethodCall) Type () Type { } func (this *MethodCall) HasExplicitType () bool { return true } func (this *MethodCall) IsConstant () bool { return false } +func (this *MethodCall) Description () string { return "method call" } func (this *MethodCall) String () string { out := fmt.Sprint(this.Source, ".[", this.Name) for _, argument := range this.Arguments { @@ -165,6 +172,7 @@ func (this *Subscript) Position () errors.Position { return this.Pos } func (this *Subscript) Type () Type { return this.Ty } func (this *Subscript) HasExplicitType () bool { return true } func (this *Subscript) IsConstant () bool { return false } +func (this *Subscript) Description () string { return "subscript operation" } func (this *Subscript) String () string { return fmt.Sprint("[.", this.Slice, " ", this.Offset, "]") } @@ -190,6 +198,7 @@ func (this *Slice) IsConstant () bool { this.Start.IsConstant() && this.End.IsConstant() } +func (this *Slice) Description () string { return "slice operation" } func (this *Slice) String () string { out := fmt.Sprint("[\\", this.Slice, " ") if this.Start != nil { @@ -218,6 +227,7 @@ func (this *Length) Position () errors.Position { return this.Pos } func (this *Length) Type () Type { return this.Ty } func (this *Length) HasExplicitType () bool { return true } func (this *Length) IsConstant () bool { return this.Slice.IsConstant() } +func (this *Length) Description () string { return "length operation" } func (this *Length) String () string { return fmt.Sprint("[#", this.Slice, "]") } @@ -241,6 +251,7 @@ func (this *Dereference) Position () errors.Position { return this.Pos } func (this *Dereference) Type () Type { return this.Ty } func (this *Dereference) HasExplicitType () bool { return true } func (this *Dereference) IsConstant () bool { return false } +func (this *Dereference) Description () string { return "dereference operation" } func (this *Dereference) String () string { return fmt.Sprint("[.", this.Pointer, "]") } @@ -266,6 +277,7 @@ func (this *Reference) Position () errors.Position { return this.Pos } func (this *Reference) Type () Type { return this.Ty } func (this *Reference) HasExplicitType () bool { return true } func (this *Reference) IsConstant () bool { return false } +func (this *Reference) Description () string { return "reference operation" } func (this *Reference) String () string { return fmt.Sprint("[@", this.Value, "]") } @@ -284,6 +296,7 @@ func (this *ValueCast) Position () errors.Position { return this.Pos } func (this *ValueCast) Type () Type { return this.Ty } func (this *ValueCast) HasExplicitType () bool { return true } func (this *ValueCast) IsConstant () bool { return this.Value.IsConstant() } +func (this *ValueCast) Description () string { return "value cast" } func (this *ValueCast) String () string { return fmt.Sprint("[~ ", this.Ty, this.Value, "]") } @@ -303,6 +316,7 @@ func (this *BitCast) Position () errors.Position { return this.Pos } func (this *BitCast) Type () Type { return this.Ty } func (this *BitCast) HasExplicitType () bool { return true } func (this *BitCast) IsConstant () bool { return this.Value.IsConstant() } +func (this *BitCast) Description () string { return "bit cast" } func (this *BitCast) String () string { return fmt.Sprint("[~~ ", this.Ty, this.Value, "]") } @@ -333,6 +347,9 @@ func (this *Operation) IsConstant () bool { } return true } +func (this *Operation) Description () string { + return fmt.Sprintf("%v operation", this.Operator) +} func (this *Operation) String () string { out := fmt.Sprint("[", this.Operator) for _, argument := range this.Arguments { @@ -367,6 +384,7 @@ func (this *Block) IsConstant () bool { if len(this.Steps) == 0 { return false } return this.Steps[len(this.Steps) - 1].IsConstant() } +func (this *Block) Description () string { return "block" } func (this *Block) String () string { out := "{" for index, step := range this.Steps { @@ -397,6 +415,7 @@ func (this *MemberAccess) Position () errors.Position { return this.Pos } func (this *MemberAccess) Type () Type { return this.Ty } func (this *MemberAccess) HasExplicitType () bool { return true } func (this *MemberAccess) IsConstant () bool { return this.Source.IsConstant() } +func (this *MemberAccess) Description () string { return "member access" } func (this *MemberAccess) String () string { return fmt.Sprint(this.Source, ".", this.Member) } @@ -428,6 +447,7 @@ func (this *IfElse) IsConstant () bool { this.True.IsConstant() && this.False.IsConstant() } +func (this *IfElse) Description () string { return "if/else" } func (this *IfElse) String () string { out := fmt.Sprint("if ", this.Condition, " then ", this.True) if this.False != nil { @@ -475,6 +495,7 @@ func (this *Match) IsConstant () bool { } return true } +func (this *Match) Description () string { return "match" } func (this *Match) String () string { out := fmt.Sprint("match ", this.Value) for _, cas := range this.Cases { @@ -522,6 +543,7 @@ func (this *Switch) IsConstant () bool { } return true } +func (this *Switch) Description () string { return "switch" } func (this *Switch) String () string { out := fmt.Sprint("switch ", this.Value) for _, cas := range this.Cases { @@ -566,6 +588,7 @@ func (this *Loop) HasExplicitType () bool { return false } func (this *Loop) IsConstant () bool { return false /* TODO this may be decidable */ } +func (this *Loop) Description () string { return "loop" } func (this *Loop) String () string { return fmt.Sprint("loop ", this.Body) } @@ -600,6 +623,7 @@ func (this *For) HasExplicitType () bool { return false } func (this *For) IsConstant () bool { return false /* TODO this may be decidable */ } +func (this *For) Description () string { return "for loop" } func (this *For) String () string { out := "for" if this.Index != nil { @@ -625,6 +649,7 @@ func (this *Break) Position () errors.Position { return this.Pos } func (this *Break) Type () Type { return nil } func (this *Break) HasExplicitType () bool { return false } func (this *Break) IsConstant () bool { return false } +func (this *Break) Description () string { return "break" } func (this *Break) String () string { if this.Value == nil { return "[break]" @@ -652,6 +677,7 @@ func (this *Return) Position () errors.Position { return this.Pos } func (this *Return) Type () Type { return nil } func (this *Return) HasExplicitType () bool { return false } func (this *Return) IsConstant () bool { return false } +func (this *Return) Description () string { return "return" } func (this *Return) String () string { if this.Value == nil { return "[return]" @@ -675,6 +701,7 @@ func (this *Assignment) Position () errors.Position { return this.Pos } func (this *Assignment) Type () Type { return nil } func (this *Assignment) HasExplicitType () bool { return false } func (this *Assignment) IsConstant () bool { return false } +func (this *Assignment) Description () string { return "assignment" } func (this *Assignment) String () string { return fmt.Sprint(this.Location, "=", this.Value) } @@ -700,6 +727,7 @@ func (this *Constant) Position () errors.Position { return this.Pos } func (this *Constant) Type () Type { return this.Ty } func (this *Constant) HasExplicitType () bool { return true } func (this *Constant) IsConstant () bool { return true } +func (this *Constant) Description () string { return "constant" } func (this *Constant) String () string { output := "" if this.UnitNickname != "" { diff --git a/entity/literal.go b/entity/literal.go index bbf7ba1..d612fd1 100644 --- a/entity/literal.go +++ b/entity/literal.go @@ -22,6 +22,7 @@ func (this *LiteralInt) Position () errors.Position { return this.Pos } func (this *LiteralInt) Type () Type { return this.Ty } func (this *LiteralInt) HasExplicitType () bool { return false } func (this *LiteralInt) IsConstant () bool { return true } +func (this *LiteralInt) Description () string { return "integer literal" } func (this *LiteralInt) String () string { return fmt.Sprint(this.Value) } @@ -43,6 +44,7 @@ func (*LiteralFloat) expression(){} func (this *LiteralFloat) Position () errors.Position { return this.Pos } func (this *LiteralFloat) Type () Type { return this.Ty } func (this *LiteralFloat) HasExplicitType () bool { return false } +func (this *LiteralFloat) Description () string { return "floating point literal" } func (this *LiteralFloat) IsConstant () bool { return true } func (this *LiteralFloat) String () string { return fmt.Sprint(this.Value) @@ -78,6 +80,7 @@ func (*LiteralString) expression(){} func (this *LiteralString) Position () errors.Position { return this.Pos } func (this *LiteralString) Type () Type { return this.Ty } func (this *LiteralString) HasExplicitType () bool { return false } +func (this *LiteralString) Description () string { return "sring literal" } func (this *LiteralString) IsConstant () bool { return true } func (this *LiteralString) String () string { return Quote(this.ValueUTF8) @@ -103,6 +106,7 @@ func (*LiteralArray) expression(){} func (this *LiteralArray) Position () errors.Position { return this.Pos } func (this *LiteralArray) Type () Type { return this.Ty } func (this *LiteralArray) HasExplicitType () bool { return false } +func (this *LiteralArray) Description () string { return "array literal" } func (this *LiteralArray) IsConstant () bool { for _, element := range this.Elements { if !element.IsConstant() { return false } @@ -140,6 +144,7 @@ func (*LiteralStruct) expression(){} func (this *LiteralStruct) Position () errors.Position { return this.Pos } func (this *LiteralStruct) Type () Type { return this.Ty } func (this *LiteralStruct) HasExplicitType () bool { return false } +func (this *LiteralStruct) Description () string { return "struct literal" } func (this *LiteralStruct) IsConstant () bool { for _, member := range this.Members { if !member.Value.IsConstant() { return false } @@ -171,6 +176,7 @@ func (*LiteralBoolean) expression(){} func (this *LiteralBoolean) Position () errors.Position { return this.Pos } func (this *LiteralBoolean) Type () Type { return this.Ty } func (this *LiteralBoolean) HasExplicitType () bool { return false } +func (this *LiteralBoolean) Description () string { return "boolean literal" } func (this *LiteralBoolean) IsConstant () bool { return true } func (this *LiteralBoolean) String () string { if this.Value { @@ -193,5 +199,6 @@ func (*LiteralNil) expression(){} func (this *LiteralNil) Position () errors.Position { return this.Pos } func (this *LiteralNil) Type () Type { return this.Ty } func (this *LiteralNil) HasExplicitType () bool { return false } +func (this *LiteralNil) Description () string { return "nil" } func (this *LiteralNil) IsConstant () bool { return true } func (this *LiteralNil) String () string { return "nil" }