Added a Description() method to Expression

This commit is contained in:
Sasha Koshka 2024-04-11 20:46:59 -04:00
parent 40d500b705
commit fc930fa3d4
3 changed files with 37 additions and 53 deletions

View File

@ -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())
}
}

View File

@ -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 != "" {

View File

@ -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" }