From 373db2ae815b79b9f3307c1274a782e04325cb0a Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Fri, 12 Apr 2024 00:43:24 -0400 Subject: [PATCH] Added stub for constant expression evaluator --- eval/doc.go | 2 + eval/eval.go | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 eval/doc.go create mode 100644 eval/eval.go diff --git a/eval/doc.go b/eval/doc.go new file mode 100644 index 0000000..0b28c12 --- /dev/null +++ b/eval/doc.go @@ -0,0 +1,2 @@ +// Package eval provides a way to evaluate FSPL expressions. +package eval diff --git a/eval/eval.go b/eval/eval.go new file mode 100644 index 0000000..62178c2 --- /dev/null +++ b/eval/eval.go @@ -0,0 +1,106 @@ +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 +}