github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/vars/parse.go (about)

     1  package vars
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"unicode"
     7  	"unicode/utf8"
     8  )
     9  
    10  type Valuer interface {
    11  	Value(name, params, expr string) (any, error)
    12  }
    13  
    14  type ValuerHandler func(name, params string) (any, error)
    15  
    16  func (f ValuerHandler) Value(name, params string) (any, error) { return f(name, params) }
    17  
    18  func (s Subs) Eval(valuer Valuer) (any, error) {
    19  	if len(s) == 1 && s.CountVars() == len(s) {
    20  		v := s[0].(*SubVar)
    21  		return valuer.Value(v.Name, v.Params, v.Expr)
    22  	}
    23  
    24  	value := ""
    25  	for _, sub := range s {
    26  		switch v := sub.(type) {
    27  		case *SubTxt:
    28  			value += v.Val
    29  		case *SubVar:
    30  			vv, err := valuer.Value(v.Name, v.Params, v.Expr)
    31  			if err != nil {
    32  				return nil, err
    33  			}
    34  			value += ToString(vv)
    35  		}
    36  	}
    37  
    38  	return value, nil
    39  }
    40  
    41  type SubTxt struct {
    42  	Val string
    43  }
    44  
    45  func (s SubTxt) IsVar() bool { return false }
    46  
    47  type SubVar struct {
    48  	Name   string
    49  	Params string
    50  	Expr   string
    51  }
    52  
    53  func (s SubVar) IsVar() bool { return true }
    54  
    55  type Sub interface {
    56  	IsVar() bool
    57  }
    58  
    59  type Subs []Sub
    60  
    61  func (s Subs) CountVars() (count int) {
    62  	for _, sub := range s {
    63  		if sub.IsVar() {
    64  			count++
    65  		}
    66  	}
    67  
    68  	return
    69  }
    70  
    71  func ParseExpr(src string) Subs {
    72  	s := src
    73  	var subs []Sub
    74  	left := ""
    75  	for {
    76  		a := strings.Index(s, "@")
    77  		if a < 0 || a == len(s)-1 {
    78  			left += s
    79  			break
    80  		}
    81  
    82  		left += s[:a]
    83  
    84  		a++
    85  		s = s[a:]
    86  		if s[0] == '@' {
    87  			s = s[1:]
    88  			left += "@"
    89  		} else if bracket := PairBracket(s[0]); bracket != nil {
    90  			if rb := strings.IndexByte(s[1:], bracket.Right); rb > 0 {
    91  				fn := s[1 : rb+1]
    92  				s = s[rb+2:]
    93  
    94  				subLiteral, subVar := parseName(&fn, &left, bracket)
    95  				if subLiteral != nil {
    96  					subs = append(subs, subLiteral)
    97  				}
    98  				if subVar != nil {
    99  					subs = append(subs, subVar)
   100  				}
   101  			}
   102  		} else {
   103  			subLiteral, subVar := parseName(&s, &left, bracket)
   104  			if subLiteral != nil {
   105  				subs = append(subs, subLiteral)
   106  			}
   107  			if subVar != nil {
   108  				subs = append(subs, subVar)
   109  			}
   110  		}
   111  	}
   112  
   113  	if left != "" {
   114  		subs = append(subs, &SubTxt{Val: left})
   115  	}
   116  
   117  	if Subs(subs).CountVars() == 0 {
   118  		return []Sub{&SubTxt{Val: src}}
   119  	}
   120  
   121  	return subs
   122  }
   123  
   124  type Bracket struct {
   125  	Left  byte
   126  	Right byte
   127  }
   128  
   129  func PairBracket(left byte) *Bracket {
   130  	switch left {
   131  	case '{':
   132  		return &Bracket{Left: '{', Right: '}'}
   133  	case '[':
   134  		return &Bracket{Left: '[', Right: ']'}
   135  	case '#', '%', '`':
   136  		return &Bracket{Left: left, Right: left}
   137  	case '<':
   138  		return &Bracket{Left: '<', Right: '>'}
   139  	}
   140  	return nil
   141  }
   142  
   143  func parseName(s, left *string, bracket *Bracket) (subLiteral, subVar Sub) {
   144  	original := *s
   145  	name := ""
   146  	offset := 0
   147  	for i, r := range *s {
   148  		if !validNameRune(r) {
   149  			name = (*s)[:i]
   150  			break
   151  		}
   152  		offset += utf8.RuneLen(r)
   153  	}
   154  
   155  	nonParam := name == "" && offset == len(*s)
   156  	if nonParam {
   157  		name = *s
   158  	}
   159  
   160  	if *left != "" {
   161  		subLiteral = &SubTxt{Val: *left}
   162  		*left = ""
   163  	}
   164  
   165  	sv := &SubVar{
   166  		Name: name,
   167  	}
   168  	subVar = sv
   169  
   170  	if !nonParam && offset > 0 && offset < len(*s) {
   171  		if (*s)[offset] == '(' {
   172  			if rb := strings.LastIndexByte(*s, ')'); rb > 0 {
   173  				sv.Params = (*s)[offset+1 : rb]
   174  				*s = (*s)[rb+1:]
   175  				sv.Expr = wrap(original[:rb+1], bracket)
   176  				return
   177  			}
   178  		}
   179  	}
   180  
   181  	*s = (*s)[offset:]
   182  	sv.Expr = wrap(original[:offset], bracket)
   183  
   184  	return
   185  }
   186  
   187  func wrap(s string, bracket *Bracket) string {
   188  	if bracket != nil {
   189  		return "@" + string(bracket.Left) + s + string(bracket.Right)
   190  	}
   191  
   192  	return "@" + s
   193  }
   194  
   195  func validNameRune(r int32) bool {
   196  	return unicode.IsLetter(r) || unicode.IsDigit(r) || unicode.Is(unicode.Han, r) ||
   197  		r == '_' || r == '-' || r == '.'
   198  }
   199  
   200  func ToString(value any) string {
   201  	switch vv := value.(type) {
   202  	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
   203  		return fmt.Sprintf("%d", vv)
   204  	case float32, float64:
   205  		return fmt.Sprintf("%f", vv)
   206  	case bool:
   207  		return fmt.Sprintf("%t", vv)
   208  	case string:
   209  		return vv
   210  	default:
   211  		vvv := fmt.Sprintf("%v", value)
   212  		return vvv
   213  	}
   214  }