107 lines
3.1 KiB
Go
107 lines
3.1 KiB
Go
package eval
|
|
|
|
import "git.tebibyte.media/fspl/fspl/errors"
|
|
import "git.tebibyte.media/fspl/fspl/entity"
|
|
|
|
type evaluator struct { }
|
|
|
|
func (this *evaluator) assertConstant (expression entity.Expression) error {
|
|
if expression.IsConstant() {
|
|
return nil
|
|
} else {
|
|
return errors.Errorf(expression.Position(), "%v is not constant", expression)
|
|
}
|
|
}
|
|
|
|
// EvaluateConstant evaluates a constant expression. On success, it always
|
|
// returns a literal made of other literals. It is very important that the input
|
|
// expression has come from the semantic analyzer.
|
|
func EvaluateConstant (expression entity.Expression) (entity.Expression, error) {
|
|
// evaluate
|
|
this := new(evaluator)
|
|
expression, err := this.evaluateConstant(expression)
|
|
if err != nil { return nil, err }
|
|
|
|
// sanity check
|
|
if !expression.IsConstant() {
|
|
panic("BUG: evaluation result is not constant")
|
|
}
|
|
|
|
return expression, nil
|
|
}
|
|
|
|
func (this *evaluator) evaluateConstant (expression entity.Expression) (entity.Expression, error) {
|
|
// TODO expand this to include all constant expressions
|
|
|
|
switch expression := expression.(type) {
|
|
case *entity.LiteralInt:
|
|
return expression, nil
|
|
case *entity.LiteralFloat:
|
|
return expression, nil
|
|
case *entity.LiteralString:
|
|
return expression, nil
|
|
case *entity.LiteralArray:
|
|
return this.evaluateConstantLiteralArray(expression)
|
|
case *entity.LiteralStruct:
|
|
return this.evaluateConstantLiteralStruct(expression)
|
|
case *entity.LiteralBoolean:
|
|
return expression, nil
|
|
case *entity.LiteralNil:
|
|
return expression, nil
|
|
|
|
case *entity.Constant:
|
|
return expression.Declaration.Value, nil
|
|
|
|
default:
|
|
err := this.assertConstant(expression)
|
|
if err != nil { return nil, err }
|
|
return nil, errors.Errorf (
|
|
expression.Position(),
|
|
"%v expressions are currently unsupported by the " +
|
|
"constant expression evaluator")
|
|
}
|
|
}
|
|
|
|
func (this *evaluator) evaluateConstantLiteralArray (array *entity.LiteralArray) (entity.Expression, error) {
|
|
result := &entity.LiteralArray {
|
|
Pos: array.Position(),
|
|
Elements: make([]entity.Expression, len(array.Elements)),
|
|
Ty: array.Type(),
|
|
}
|
|
|
|
for index, element := range array.Elements {
|
|
elementResult, err := this.evaluateConstant(element)
|
|
if err != nil { return nil, err }
|
|
result.Elements[index] = elementResult
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (this *evaluator) evaluateConstantLiteralStruct (struc *entity.LiteralStruct) (entity.Expression, error) {
|
|
result := &entity.LiteralStruct {
|
|
Pos: struc.Position(),
|
|
Members: make([]*entity.Member, len(struc.Members)),
|
|
MemberOrder: make([]string, len(struc.MemberOrder)),
|
|
MemberMap: make(map[string] *entity.Member),
|
|
Ty: struc.Type(),
|
|
}
|
|
|
|
for index, member := range struc.Members {
|
|
memberResult := &entity.Member {
|
|
Pos: member.Position(),
|
|
Name: member.Name,
|
|
}
|
|
|
|
memberValueResult, err := this.evaluateConstant(member.Value)
|
|
if err != nil { return nil, err }
|
|
memberResult.Value = memberValueResult
|
|
|
|
result.Members[index] = memberResult
|
|
result.MemberOrder[index] = memberResult.Name
|
|
result.MemberMap[memberResult.Name] = memberResult
|
|
}
|
|
|
|
return result, nil
|
|
}
|