123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656 |
- package pongo2
- import (
- "bytes"
- "fmt"
- "reflect"
- "strconv"
- "strings"
- )
- const (
- varTypeInt = iota
- varTypeIdent
- )
- type variablePart struct {
- typ int
- s string
- i int
- is_function_call bool
- calling_args []functionCallArgument // needed for a function call, represents all argument nodes (INode supports nested function calls)
- }
- type functionCallArgument interface {
- Evaluate(*ExecutionContext) (*Value, *Error)
- }
- // TODO: Add location tokens
- type stringResolver struct {
- location_token *Token
- val string
- }
- type intResolver struct {
- location_token *Token
- val int
- }
- type floatResolver struct {
- location_token *Token
- val float64
- }
- type boolResolver struct {
- location_token *Token
- val bool
- }
- type variableResolver struct {
- location_token *Token
- parts []*variablePart
- }
- type nodeFilteredVariable struct {
- location_token *Token
- resolver IEvaluator
- filterChain []*filterCall
- }
- type nodeVariable struct {
- location_token *Token
- expr IEvaluator
- }
- func (expr *nodeFilteredVariable) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error {
- value, err := expr.Evaluate(ctx)
- if err != nil {
- return err
- }
- buffer.WriteString(value.String())
- return nil
- }
- func (expr *variableResolver) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error {
- value, err := expr.Evaluate(ctx)
- if err != nil {
- return err
- }
- buffer.WriteString(value.String())
- return nil
- }
- func (expr *stringResolver) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error {
- value, err := expr.Evaluate(ctx)
- if err != nil {
- return err
- }
- buffer.WriteString(value.String())
- return nil
- }
- func (expr *intResolver) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error {
- value, err := expr.Evaluate(ctx)
- if err != nil {
- return err
- }
- buffer.WriteString(value.String())
- return nil
- }
- func (expr *floatResolver) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error {
- value, err := expr.Evaluate(ctx)
- if err != nil {
- return err
- }
- buffer.WriteString(value.String())
- return nil
- }
- func (expr *boolResolver) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error {
- value, err := expr.Evaluate(ctx)
- if err != nil {
- return err
- }
- buffer.WriteString(value.String())
- return nil
- }
- func (v *nodeFilteredVariable) GetPositionToken() *Token {
- return v.location_token
- }
- func (v *variableResolver) GetPositionToken() *Token {
- return v.location_token
- }
- func (v *stringResolver) GetPositionToken() *Token {
- return v.location_token
- }
- func (v *intResolver) GetPositionToken() *Token {
- return v.location_token
- }
- func (v *floatResolver) GetPositionToken() *Token {
- return v.location_token
- }
- func (v *boolResolver) GetPositionToken() *Token {
- return v.location_token
- }
- func (s *stringResolver) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
- return AsValue(s.val), nil
- }
- func (i *intResolver) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
- return AsValue(i.val), nil
- }
- func (f *floatResolver) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
- return AsValue(f.val), nil
- }
- func (b *boolResolver) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
- return AsValue(b.val), nil
- }
- func (s *stringResolver) FilterApplied(name string) bool {
- return false
- }
- func (i *intResolver) FilterApplied(name string) bool {
- return false
- }
- func (f *floatResolver) FilterApplied(name string) bool {
- return false
- }
- func (b *boolResolver) FilterApplied(name string) bool {
- return false
- }
- func (nv *nodeVariable) FilterApplied(name string) bool {
- return nv.expr.FilterApplied(name)
- }
- func (nv *nodeVariable) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error {
- value, err := nv.expr.Evaluate(ctx)
- if err != nil {
- return err
- }
- if !nv.expr.FilterApplied("safe") && !value.safe && value.IsString() && ctx.Autoescape {
- // apply escape filter
- value, err = filters["escape"](value, nil)
- if err != nil {
- return err
- }
- }
- buffer.WriteString(value.String())
- return nil
- }
- func (vr *variableResolver) FilterApplied(name string) bool {
- return false
- }
- func (vr *variableResolver) String() string {
- parts := make([]string, 0, len(vr.parts))
- for _, p := range vr.parts {
- switch p.typ {
- case varTypeInt:
- parts = append(parts, strconv.Itoa(p.i))
- case varTypeIdent:
- parts = append(parts, p.s)
- default:
- panic("unimplemented")
- }
- }
- return strings.Join(parts, ".")
- }
- func (vr *variableResolver) resolve(ctx *ExecutionContext) (*Value, error) {
- var current reflect.Value
- var is_safe bool
- for idx, part := range vr.parts {
- if idx == 0 {
- // We're looking up the first part of the variable.
- // First we're having a look in our private
- // context (e. g. information provided by tags, like the forloop)
- val, in_private := ctx.Private[vr.parts[0].s]
- if !in_private {
- // Nothing found? Then have a final lookup in the public context
- val = ctx.Public[vr.parts[0].s]
- }
- current = reflect.ValueOf(val) // Get the initial value
- } else {
- // Next parts, resolve it from current
- // Before resolving the pointer, let's see if we have a method to call
- // Problem with resolving the pointer is we're changing the receiver
- is_func := false
- if part.typ == varTypeIdent {
- func_value := current.MethodByName(part.s)
- if func_value.IsValid() {
- current = func_value
- is_func = true
- }
- }
- if !is_func {
- // If current a pointer, resolve it
- if current.Kind() == reflect.Ptr {
- current = current.Elem()
- if !current.IsValid() {
- // Value is not valid (anymore)
- return AsValue(nil), nil
- }
- }
- // Look up which part must be called now
- switch part.typ {
- case varTypeInt:
- // Calling an index is only possible for:
- // * slices/arrays/strings
- switch current.Kind() {
- case reflect.String, reflect.Array, reflect.Slice:
- current = current.Index(part.i)
- default:
- return nil, fmt.Errorf("Can't access an index on type %s (variable %s)",
- current.Kind().String(), vr.String())
- }
- case varTypeIdent:
- // debugging:
- // fmt.Printf("now = %s (kind: %s)\n", part.s, current.Kind().String())
- // Calling a field or key
- switch current.Kind() {
- case reflect.Struct:
- current = current.FieldByName(part.s)
- case reflect.Map:
- current = current.MapIndex(reflect.ValueOf(part.s))
- default:
- return nil, fmt.Errorf("Can't access a field by name on type %s (variable %s)",
- current.Kind().String(), vr.String())
- }
- default:
- panic("unimplemented")
- }
- }
- }
- if !current.IsValid() {
- // Value is not valid (anymore)
- return AsValue(nil), nil
- }
- // If current is a reflect.ValueOf(pongo2.Value), then unpack it
- // Happens in function calls (as a return value) or by injecting
- // into the execution context (e.g. in a for-loop)
- if current.Type() == reflect.TypeOf(&Value{}) {
- tmp_value := current.Interface().(*Value)
- current = tmp_value.val
- is_safe = tmp_value.safe
- }
- // Check whether this is an interface and resolve it where required
- if current.Kind() == reflect.Interface {
- current = reflect.ValueOf(current.Interface())
- }
- // Check if the part is a function call
- if part.is_function_call || current.Kind() == reflect.Func {
- // Check for callable
- if current.Kind() != reflect.Func {
- return nil, fmt.Errorf("'%s' is not a function (it is %s).", vr.String(), current.Kind().String())
- }
- // Check for correct function syntax and types
- // func(*Value, ...) *Value
- t := current.Type()
- // Input arguments
- if len(part.calling_args) != t.NumIn() && !(len(part.calling_args) >= t.NumIn()-1 && t.IsVariadic()) {
- return nil,
- fmt.Errorf("Function input argument count (%d) of '%s' must be equal to the calling argument count (%d).",
- t.NumIn(), vr.String(), len(part.calling_args))
- }
- // Output arguments
- if t.NumOut() != 1 {
- return nil, fmt.Errorf("'%s' must have exactly 1 output argument.", vr.String())
- }
- // Evaluate all parameters
- parameters := make([]reflect.Value, 0)
- num_args := t.NumIn()
- is_variadic := t.IsVariadic()
- var fn_arg reflect.Type
- for idx, arg := range part.calling_args {
- pv, err := arg.Evaluate(ctx)
- if err != nil {
- return nil, err
- }
- if is_variadic {
- if idx >= t.NumIn()-1 {
- fn_arg = t.In(num_args - 1).Elem()
- } else {
- fn_arg = t.In(idx)
- }
- } else {
- fn_arg = t.In(idx)
- }
- if fn_arg != reflect.TypeOf(new(Value)) {
- // Function's argument is not a *pongo2.Value, then we have to check whether input argument is of the same type as the function's argument
- if !is_variadic {
- if fn_arg != reflect.TypeOf(pv.Interface()) && fn_arg.Kind() != reflect.Interface {
- return nil, fmt.Errorf("Function input argument %d of '%s' must be of type %s or *pongo2.Value (not %T).",
- idx, vr.String(), fn_arg.String(), pv.Interface())
- } else {
- // Function's argument has another type, using the interface-value
- parameters = append(parameters, reflect.ValueOf(pv.Interface()))
- }
- } else {
- if fn_arg != reflect.TypeOf(pv.Interface()) && fn_arg.Kind() != reflect.Interface {
- return nil, fmt.Errorf("Function variadic input argument of '%s' must be of type %s or *pongo2.Value (not %T).",
- vr.String(), fn_arg.String(), pv.Interface())
- } else {
- // Function's argument has another type, using the interface-value
- parameters = append(parameters, reflect.ValueOf(pv.Interface()))
- }
- }
- } else {
- // Function's argument is a *pongo2.Value
- parameters = append(parameters, reflect.ValueOf(pv))
- }
- }
- // Call it and get first return parameter back
- rv := current.Call(parameters)[0]
- if rv.Type() != reflect.TypeOf(new(Value)) {
- current = reflect.ValueOf(rv.Interface())
- } else {
- // Return the function call value
- current = rv.Interface().(*Value).val
- is_safe = rv.Interface().(*Value).safe
- }
- }
- }
- if !current.IsValid() {
- // Value is not valid (e. g. NIL value)
- return AsValue(nil), nil
- }
- return &Value{val: current, safe: is_safe}, nil
- }
- func (vr *variableResolver) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
- value, err := vr.resolve(ctx)
- if err != nil {
- return AsValue(nil), ctx.Error(err.Error(), vr.location_token)
- }
- return value, nil
- }
- func (v *nodeFilteredVariable) FilterApplied(name string) bool {
- for _, filter := range v.filterChain {
- if filter.name == name {
- return true
- }
- }
- return false
- }
- func (v *nodeFilteredVariable) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
- value, err := v.resolver.Evaluate(ctx)
- if err != nil {
- return nil, err
- }
- for _, filter := range v.filterChain {
- value, err = filter.Execute(value, ctx)
- if err != nil {
- return nil, err
- }
- }
- return value, nil
- }
- // IDENT | IDENT.(IDENT|NUMBER)...
- func (p *Parser) parseVariableOrLiteral() (IEvaluator, *Error) {
- t := p.Current()
- if t == nil {
- return nil, p.Error("Unexpected EOF, expected a number, string, keyword or identifier.", p.last_token)
- }
- // Is first part a number or a string, there's nothing to resolve (because there's only to return the value then)
- switch t.Typ {
- case TokenNumber:
- p.Consume()
- // One exception to the rule that we don't have float64 literals is at the beginning
- // of an expression (or a variable name). Since we know we started with an integer
- // which can't obviously be a variable name, we can check whether the first number
- // is followed by dot (and then a number again). If so we're converting it to a float64.
- if p.Match(TokenSymbol, ".") != nil {
- // float64
- t2 := p.MatchType(TokenNumber)
- if t2 == nil {
- return nil, p.Error("Expected a number after the '.'.", nil)
- }
- f, err := strconv.ParseFloat(fmt.Sprintf("%s.%s", t.Val, t2.Val), 64)
- if err != nil {
- return nil, p.Error(err.Error(), t)
- }
- fr := &floatResolver{
- location_token: t,
- val: f,
- }
- return fr, nil
- } else {
- i, err := strconv.Atoi(t.Val)
- if err != nil {
- return nil, p.Error(err.Error(), t)
- }
- nr := &intResolver{
- location_token: t,
- val: i,
- }
- return nr, nil
- }
- case TokenString:
- p.Consume()
- sr := &stringResolver{
- location_token: t,
- val: t.Val,
- }
- return sr, nil
- case TokenKeyword:
- p.Consume()
- switch t.Val {
- case "true":
- br := &boolResolver{
- location_token: t,
- val: true,
- }
- return br, nil
- case "false":
- br := &boolResolver{
- location_token: t,
- val: false,
- }
- return br, nil
- default:
- return nil, p.Error("This keyword is not allowed here.", nil)
- }
- }
- resolver := &variableResolver{
- location_token: t,
- }
- // First part of a variable MUST be an identifier
- if t.Typ != TokenIdentifier {
- return nil, p.Error("Expected either a number, string, keyword or identifier.", t)
- }
- resolver.parts = append(resolver.parts, &variablePart{
- typ: varTypeIdent,
- s: t.Val,
- })
- p.Consume() // we consumed the first identifier of the variable name
- variableLoop:
- for p.Remaining() > 0 {
- t = p.Current()
- if p.Match(TokenSymbol, ".") != nil {
- // Next variable part (can be either NUMBER or IDENT)
- t2 := p.Current()
- if t2 != nil {
- switch t2.Typ {
- case TokenIdentifier:
- resolver.parts = append(resolver.parts, &variablePart{
- typ: varTypeIdent,
- s: t2.Val,
- })
- p.Consume() // consume: IDENT
- continue variableLoop
- case TokenNumber:
- i, err := strconv.Atoi(t2.Val)
- if err != nil {
- return nil, p.Error(err.Error(), t2)
- }
- resolver.parts = append(resolver.parts, &variablePart{
- typ: varTypeInt,
- i: i,
- })
- p.Consume() // consume: NUMBER
- continue variableLoop
- default:
- return nil, p.Error("This token is not allowed within a variable name.", t2)
- }
- } else {
- // EOF
- return nil, p.Error("Unexpected EOF, expected either IDENTIFIER or NUMBER after DOT.",
- p.last_token)
- }
- } else if p.Match(TokenSymbol, "(") != nil {
- // Function call
- // FunctionName '(' Comma-separated list of expressions ')'
- part := resolver.parts[len(resolver.parts)-1]
- part.is_function_call = true
- argumentLoop:
- for {
- if p.Remaining() == 0 {
- return nil, p.Error("Unexpected EOF, expected function call argument list.", p.last_token)
- }
- if p.Peek(TokenSymbol, ")") == nil {
- // No closing bracket, so we're parsing an expression
- expr_arg, err := p.ParseExpression()
- if err != nil {
- return nil, err
- }
- part.calling_args = append(part.calling_args, expr_arg)
- if p.Match(TokenSymbol, ")") != nil {
- // If there's a closing bracket after an expression, we will stop parsing the arguments
- break argumentLoop
- } else {
- // If there's NO closing bracket, there MUST be an comma
- if p.Match(TokenSymbol, ",") == nil {
- return nil, p.Error("Missing comma or closing bracket after argument.", nil)
- }
- }
- } else {
- // We got a closing bracket, so stop parsing arguments
- p.Consume()
- break argumentLoop
- }
- }
- // We're done parsing the function call, next variable part
- continue variableLoop
- }
- // No dot or function call? Then we're done with the variable parsing
- break
- }
- return resolver, nil
- }
- func (p *Parser) parseVariableOrLiteralWithFilter() (*nodeFilteredVariable, *Error) {
- v := &nodeFilteredVariable{
- location_token: p.Current(),
- }
- // Parse the variable name
- resolver, err := p.parseVariableOrLiteral()
- if err != nil {
- return nil, err
- }
- v.resolver = resolver
- // Parse all the filters
- filterLoop:
- for p.Match(TokenSymbol, "|") != nil {
- // Parse one single filter
- filter, err := p.parseFilter()
- if err != nil {
- return nil, err
- }
- // Check sandbox filter restriction
- if _, is_banned := p.template.set.bannedFilters[filter.name]; is_banned {
- return nil, p.Error(fmt.Sprintf("Usage of filter '%s' is not allowed (sandbox restriction active).", filter.name), nil)
- }
- v.filterChain = append(v.filterChain, filter)
- continue filterLoop
- return nil, p.Error("This token is not allowed within a variable.", nil)
- }
- return v, nil
- }
- func (p *Parser) parseVariableElement() (INode, *Error) {
- node := &nodeVariable{
- location_token: p.Current(),
- }
- p.Consume() // consume '{{'
- expr, err := p.ParseExpression()
- if err != nil {
- return nil, err
- }
- node.expr = expr
- if p.Match(TokenSymbol, "}}") == nil {
- return nil, p.Error("'}}' expected", nil)
- }
- return node, nil
- }
|