2023-01-10 11:27:13 +00:00
|
|
|
package queryexpr
|
|
|
|
|
|
|
|
import (
|
2023-04-16 22:31:03 +00:00
|
|
|
"github.com/lmika/dynamo-browse/internal/common/sliceutils"
|
|
|
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models"
|
2023-01-10 11:27:13 +00:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (r *astSubRef) evalToIR(ctx *evalContext, info *models.TableInfo) (irAtom, error) {
|
|
|
|
refIR, err := r.Ref.evalToIR(ctx, info)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-02-16 10:57:40 +00:00
|
|
|
if len(r.SubRefs) == 0 {
|
2023-01-10 11:27:13 +00:00
|
|
|
return refIR, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// This node has subrefs
|
|
|
|
namePath, isNamePath := refIR.(irNamePath)
|
|
|
|
if !isNamePath {
|
|
|
|
return nil, OperandNotANameError(r.String())
|
|
|
|
}
|
|
|
|
|
2023-02-16 10:57:40 +00:00
|
|
|
quals := make([]any, 0)
|
|
|
|
for _, sr := range r.SubRefs {
|
|
|
|
sv, err := sr.evalToStrOrInt(ctx, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
quals = append(quals, sv)
|
2023-01-10 11:27:13 +00:00
|
|
|
}
|
|
|
|
return irNamePath{name: namePath.name, quals: quals}, nil
|
|
|
|
}
|
|
|
|
|
2023-04-14 05:35:43 +00:00
|
|
|
func (r *astSubRef) evalItem(ctx *evalContext, item models.Item) (exprValue, error) {
|
2023-01-10 11:27:13 +00:00
|
|
|
res, err := r.Ref.evalItem(ctx, item)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-02-16 10:57:40 +00:00
|
|
|
res, err = r.evalSubRefs(ctx, item, res, r.SubRefs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-01-10 11:27:13 +00:00
|
|
|
|
2023-02-16 10:57:40 +00:00
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
2023-04-14 05:35:43 +00:00
|
|
|
func (r *astSubRef) evalSubRefs(ctx *evalContext, item models.Item, res exprValue, subRefs []*astSubRefType) (exprValue, error) {
|
2023-02-16 10:57:40 +00:00
|
|
|
for i, sr := range subRefs {
|
|
|
|
sv, err := sr.evalToStrOrInt(ctx, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2023-01-10 11:27:13 +00:00
|
|
|
}
|
|
|
|
|
2023-02-16 10:57:40 +00:00
|
|
|
switch val := sv.(type) {
|
|
|
|
case string:
|
2023-04-14 05:35:43 +00:00
|
|
|
mapRes, isMapRes := res.(mappableExprValue)
|
2023-02-16 10:57:40 +00:00
|
|
|
if !isMapRes {
|
|
|
|
return nil, newValueNotAMapError(r, subRefs[:i+1])
|
|
|
|
}
|
|
|
|
|
2023-04-14 05:35:43 +00:00
|
|
|
if mapRes.hasKey(val) {
|
|
|
|
res, err = mapRes.valueOf(val)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
res = nil
|
2023-02-16 10:57:40 +00:00
|
|
|
}
|
2023-04-14 05:35:43 +00:00
|
|
|
case int64:
|
|
|
|
listRes, isMapRes := res.(slicableExprValue)
|
2023-02-16 10:57:40 +00:00
|
|
|
if !isMapRes {
|
|
|
|
return nil, newValueNotAListError(r, subRefs[:i+1])
|
|
|
|
}
|
|
|
|
|
2023-04-14 05:35:43 +00:00
|
|
|
// TODO - deal with index properly (i.e. error handling)
|
|
|
|
res, err = listRes.valueAt(int(val))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-01-10 11:27:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *astSubRef) canModifyItem(ctx *evalContext, item models.Item) bool {
|
|
|
|
return r.Ref.canModifyItem(ctx, item)
|
|
|
|
}
|
|
|
|
|
2023-04-14 05:35:43 +00:00
|
|
|
func (r *astSubRef) setEvalItem(ctx *evalContext, item models.Item, value exprValue) error {
|
2023-02-16 10:57:40 +00:00
|
|
|
if len(r.SubRefs) == 0 {
|
2023-01-10 11:27:13 +00:00
|
|
|
return r.Ref.setEvalItem(ctx, item, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
parentItem, err := r.Ref.evalItem(ctx, item)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-02-16 10:57:40 +00:00
|
|
|
if len(r.SubRefs) > 1 {
|
|
|
|
parentItem, err = r.evalSubRefs(ctx, item, parentItem, r.SubRefs[0:len(r.SubRefs)-1])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2023-01-10 11:27:13 +00:00
|
|
|
}
|
2023-02-16 10:57:40 +00:00
|
|
|
}
|
2023-01-10 11:27:13 +00:00
|
|
|
|
2023-02-16 10:57:40 +00:00
|
|
|
sv, err := r.SubRefs[len(r.SubRefs)-1].evalToStrOrInt(ctx, nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch val := sv.(type) {
|
|
|
|
case string:
|
2023-04-14 05:35:43 +00:00
|
|
|
mapRes, isMapRes := parentItem.(modifiableMapExprValue)
|
2023-02-16 10:57:40 +00:00
|
|
|
if !isMapRes {
|
|
|
|
return newValueNotAMapError(r, r.SubRefs)
|
|
|
|
}
|
|
|
|
|
2023-04-14 05:35:43 +00:00
|
|
|
mapRes.setValueOf(val, value)
|
|
|
|
case int64:
|
|
|
|
listRes, isMapRes := parentItem.(modifiableSliceExprValue)
|
2023-02-16 10:57:40 +00:00
|
|
|
if !isMapRes {
|
|
|
|
return newValueNotAListError(r, r.SubRefs)
|
2023-01-10 11:27:13 +00:00
|
|
|
}
|
2023-02-16 10:57:40 +00:00
|
|
|
|
2023-04-14 05:35:43 +00:00
|
|
|
listRes.setValueAt(int(val), value)
|
2023-01-10 11:27:13 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *astSubRef) deleteAttribute(ctx *evalContext, item models.Item) error {
|
2023-02-16 10:57:40 +00:00
|
|
|
if len(r.SubRefs) == 0 {
|
2023-01-10 11:27:13 +00:00
|
|
|
return r.Ref.deleteAttribute(ctx, item)
|
|
|
|
}
|
|
|
|
|
|
|
|
parentItem, err := r.Ref.evalItem(ctx, item)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-02-16 10:57:40 +00:00
|
|
|
if len(r.SubRefs) > 1 {
|
|
|
|
parentItem, err = r.evalSubRefs(ctx, item, parentItem, r.SubRefs[0:len(r.SubRefs)-1])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sv, err := r.SubRefs[len(r.SubRefs)-1].evalToStrOrInt(ctx, nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-01-10 11:27:13 +00:00
|
|
|
|
2023-02-16 10:57:40 +00:00
|
|
|
switch val := sv.(type) {
|
|
|
|
case string:
|
2023-04-14 05:35:43 +00:00
|
|
|
mapRes, isMapRes := parentItem.(modifiableMapExprValue)
|
2023-02-16 10:57:40 +00:00
|
|
|
if !isMapRes {
|
|
|
|
return newValueNotAMapError(r, r.SubRefs)
|
|
|
|
}
|
|
|
|
|
2023-04-14 05:35:43 +00:00
|
|
|
mapRes.deleteValueOf(val)
|
|
|
|
case int64:
|
|
|
|
listRes, isMapRes := parentItem.(modifiableSliceExprValue)
|
2023-02-16 10:57:40 +00:00
|
|
|
if !isMapRes {
|
|
|
|
return newValueNotAListError(r, r.SubRefs)
|
2023-01-10 11:27:13 +00:00
|
|
|
}
|
2023-02-16 10:57:40 +00:00
|
|
|
|
|
|
|
// TODO: handle indexes out of bounds
|
2023-04-14 05:35:43 +00:00
|
|
|
listRes.deleteValueAt(int(val))
|
2023-01-10 11:27:13 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *astSubRef) String() string {
|
|
|
|
var sb strings.Builder
|
|
|
|
|
|
|
|
sb.WriteString(r.Ref.String())
|
2023-02-16 10:57:40 +00:00
|
|
|
for _, q := range r.SubRefs {
|
|
|
|
switch {
|
|
|
|
case q.DotQual != "":
|
|
|
|
sb.WriteRune('.')
|
|
|
|
sb.WriteString(q.DotQual)
|
|
|
|
case q.SubIndex != nil:
|
|
|
|
sb.WriteRune('[')
|
|
|
|
sb.WriteString(q.SubIndex.String())
|
|
|
|
sb.WriteRune(']')
|
|
|
|
}
|
2023-01-10 11:27:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return sb.String()
|
|
|
|
}
|
2023-02-16 10:57:40 +00:00
|
|
|
|
|
|
|
func (sr *astSubRefType) evalToStrOrInt(ctx *evalContext, item models.Item) (any, error) {
|
|
|
|
if sr.DotQual != "" {
|
|
|
|
return sr.DotQual, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
subEvalItem, err := sr.SubIndex.evalItem(ctx, item)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
switch v := subEvalItem.(type) {
|
2023-04-14 05:35:43 +00:00
|
|
|
case stringableExprValue:
|
|
|
|
return v.asString(), nil
|
|
|
|
case numberableExprValue:
|
|
|
|
return v.asInt(), nil
|
2023-02-16 10:57:40 +00:00
|
|
|
}
|
|
|
|
return nil, ValueNotUsableAsASubref{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sr *astSubRefType) string() string {
|
|
|
|
switch {
|
|
|
|
case sr.DotQual != "":
|
|
|
|
return sr.DotQual
|
|
|
|
case sr.SubIndex != nil:
|
|
|
|
return sr.SubIndex.String()
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func newValueNotAMapError(r *astSubRef, subRefs []*astSubRefType) ValueNotAMapError {
|
|
|
|
subRefStrings := sliceutils.Map(subRefs, func(srt *astSubRefType) string {
|
|
|
|
return srt.string()
|
|
|
|
})
|
|
|
|
return ValueNotAMapError(append([]string{r.Ref.String()}, subRefStrings...))
|
|
|
|
}
|
|
|
|
|
|
|
|
func newValueNotAListError(r *astSubRef, subRefs []*astSubRefType) ValueNotAListError {
|
|
|
|
subRefStrings := sliceutils.Map(subRefs, func(srt *astSubRefType) string {
|
|
|
|
return srt.string()
|
|
|
|
})
|
|
|
|
return ValueNotAListError(append([]string{r.Ref.String()}, subRefStrings...))
|
|
|
|
}
|