github.com/MontFerret/ferret@v0.18.0/pkg/runtime/expressions/member.go (about)

     1  package expressions
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/pkg/errors"
     7  
     8  	"github.com/MontFerret/ferret/pkg/runtime/core"
     9  	"github.com/MontFerret/ferret/pkg/runtime/values"
    10  )
    11  
    12  type MemberExpression struct {
    13  	src             core.SourceMap
    14  	source          core.Expression
    15  	path            []*MemberPathSegment
    16  	preCompiledPath []core.Value
    17  }
    18  
    19  func NewMemberExpression(src core.SourceMap, source core.Expression, path []*MemberPathSegment, preCompiledPath []core.Value) (*MemberExpression, error) {
    20  	if source == nil {
    21  		return nil, core.Error(core.ErrMissedArgument, "source")
    22  	}
    23  
    24  	if len(path) == 0 {
    25  		return nil, core.Error(core.ErrMissedArgument, "path expressions")
    26  	}
    27  
    28  	return &MemberExpression{src, source, path, preCompiledPath}, nil
    29  }
    30  
    31  func (e *MemberExpression) Exec(ctx context.Context, scope *core.Scope) (core.Value, error) {
    32  	member, err := e.source.Exec(ctx, scope)
    33  
    34  	if err != nil {
    35  		if e.path[0].optional {
    36  			return values.None, nil
    37  		}
    38  
    39  		return values.None, core.SourceError(
    40  			e.src,
    41  			err,
    42  		)
    43  	}
    44  
    45  	var segments = e.preCompiledPath
    46  
    47  	if e.preCompiledPath == nil {
    48  		segments = make([]core.Value, len(e.path))
    49  
    50  		// unfold the path
    51  		for i, seg := range e.path {
    52  			segment, err := seg.exp.Exec(ctx, scope)
    53  
    54  			if err != nil {
    55  				return values.None, err
    56  			}
    57  
    58  			segments[i] = segment
    59  		}
    60  	}
    61  
    62  	var pathErr core.PathError
    63  	var out core.Value = values.None
    64  
    65  	getter, ok := member.(core.Getter)
    66  
    67  	if ok {
    68  		out, pathErr = getter.GetIn(ctx, segments)
    69  	} else {
    70  		out, pathErr = values.GetIn(ctx, member, segments)
    71  	}
    72  
    73  	if pathErr != nil {
    74  		segmentIdx := pathErr.Segment()
    75  		// if invalid index is returned, we ignore the optionality check
    76  		// and return the pathErr
    77  		if segmentIdx >= len(e.path) {
    78  			return values.None, errors.New(pathErr.Format(segments))
    79  		}
    80  
    81  		segment := e.path[segmentIdx]
    82  
    83  		if !segment.optional {
    84  			return values.None, errors.New(pathErr.Format(segments))
    85  		}
    86  
    87  		return values.None, nil
    88  	}
    89  
    90  	return out, nil
    91  }