github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/flosch/pongo2.v3/parser.go (about)

     1  package pongo2
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"strings"
     7  )
     8  
     9  type INode interface {
    10  	Execute(*ExecutionContext, *bytes.Buffer) *Error
    11  }
    12  
    13  type IEvaluator interface {
    14  	INode
    15  	GetPositionToken() *Token
    16  	Evaluate(*ExecutionContext) (*Value, *Error)
    17  	FilterApplied(name string) bool
    18  }
    19  
    20  // The parser provides you a comprehensive and easy tool to
    21  // work with the template document and arguments provided by
    22  // the user for your custom tag.
    23  //
    24  // The parser works on a token list which will be provided by pongo2.
    25  // A token is a unit you can work with. Tokens are either of type identifier,
    26  // string, number, keyword, HTML or symbol.
    27  //
    28  // (See Token's documentation for more about tokens)
    29  type Parser struct {
    30  	name       string
    31  	idx        int
    32  	tokens     []*Token
    33  	last_token *Token
    34  
    35  	// if the parser parses a template document, here will be
    36  	// a reference to it (needed to access the template through Tags)
    37  	template *Template
    38  }
    39  
    40  // Creates a new parser to parse tokens.
    41  // Used inside pongo2 to parse documents and to provide an easy-to-use
    42  // parser for tag authors
    43  func newParser(name string, tokens []*Token, template *Template) *Parser {
    44  	p := &Parser{
    45  		name:     name,
    46  		tokens:   tokens,
    47  		template: template,
    48  	}
    49  	if len(tokens) > 0 {
    50  		p.last_token = tokens[len(tokens)-1]
    51  	}
    52  	return p
    53  }
    54  
    55  // Consume one token. It will be gone forever.
    56  func (p *Parser) Consume() {
    57  	p.ConsumeN(1)
    58  }
    59  
    60  // Consume N tokens. They will be gone forever.
    61  func (p *Parser) ConsumeN(count int) {
    62  	p.idx += count
    63  }
    64  
    65  // Returns the current token.
    66  func (p *Parser) Current() *Token {
    67  	return p.Get(p.idx)
    68  }
    69  
    70  // Returns the CURRENT token if the given type matches.
    71  // Consumes this token on success.
    72  func (p *Parser) MatchType(typ TokenType) *Token {
    73  	if t := p.PeekType(typ); t != nil {
    74  		p.Consume()
    75  		return t
    76  	}
    77  	return nil
    78  }
    79  
    80  // Returns the CURRENT token if the given type AND value matches.
    81  // Consumes this token on success.
    82  func (p *Parser) Match(typ TokenType, val string) *Token {
    83  	if t := p.Peek(typ, val); t != nil {
    84  		p.Consume()
    85  		return t
    86  	}
    87  	return nil
    88  }
    89  
    90  // Returns the CURRENT token if the given type AND *one* of
    91  // the given values matches.
    92  // Consumes this token on success.
    93  func (p *Parser) MatchOne(typ TokenType, vals ...string) *Token {
    94  	for _, val := range vals {
    95  		if t := p.Peek(typ, val); t != nil {
    96  			p.Consume()
    97  			return t
    98  		}
    99  	}
   100  	return nil
   101  }
   102  
   103  // Returns the CURRENT token if the given type matches.
   104  // It DOES NOT consume the token.
   105  func (p *Parser) PeekType(typ TokenType) *Token {
   106  	return p.PeekTypeN(0, typ)
   107  }
   108  
   109  // Returns the CURRENT token if the given type AND value matches.
   110  // It DOES NOT consume the token.
   111  func (p *Parser) Peek(typ TokenType, val string) *Token {
   112  	return p.PeekN(0, typ, val)
   113  }
   114  
   115  // Returns the CURRENT token if the given type AND *one* of
   116  // the given values matches.
   117  // It DOES NOT consume the token.
   118  func (p *Parser) PeekOne(typ TokenType, vals ...string) *Token {
   119  	for _, v := range vals {
   120  		t := p.PeekN(0, typ, v)
   121  		if t != nil {
   122  			return t
   123  		}
   124  	}
   125  	return nil
   126  }
   127  
   128  // Returns the tokens[current position + shift] token if the
   129  // given type AND value matches for that token.
   130  // DOES NOT consume the token.
   131  func (p *Parser) PeekN(shift int, typ TokenType, val string) *Token {
   132  	t := p.Get(p.idx + shift)
   133  	if t != nil {
   134  		if t.Typ == typ && t.Val == val {
   135  			return t
   136  		}
   137  	}
   138  	return nil
   139  }
   140  
   141  // Returns the tokens[current position + shift] token if the given type matches.
   142  // DOES NOT consume the token for that token.
   143  func (p *Parser) PeekTypeN(shift int, typ TokenType) *Token {
   144  	t := p.Get(p.idx + shift)
   145  	if t != nil {
   146  		if t.Typ == typ {
   147  			return t
   148  		}
   149  	}
   150  	return nil
   151  }
   152  
   153  // Returns the UNCONSUMED token count.
   154  func (p *Parser) Remaining() int {
   155  	return len(p.tokens) - p.idx
   156  }
   157  
   158  // Returns the total token count.
   159  func (p *Parser) Count() int {
   160  	return len(p.tokens)
   161  }
   162  
   163  // Returns tokens[i] or NIL (if i >= len(tokens))
   164  func (p *Parser) Get(i int) *Token {
   165  	if i < len(p.tokens) {
   166  		return p.tokens[i]
   167  	}
   168  	return nil
   169  }
   170  
   171  // Returns tokens[current-position + shift] or NIL
   172  // (if (current-position + i) >= len(tokens))
   173  func (p *Parser) GetR(shift int) *Token {
   174  	i := p.idx + shift
   175  	return p.Get(i)
   176  }
   177  
   178  // Produces a nice error message and returns an error-object.
   179  // The 'token'-argument is optional. If provided, it will take
   180  // the token's position information. If not provided, it will
   181  // automatically use the CURRENT token's position information.
   182  func (p *Parser) Error(msg string, token *Token) *Error {
   183  	if token == nil {
   184  		// Set current token
   185  		token = p.Current()
   186  		if token == nil {
   187  			// Set to last token
   188  			if len(p.tokens) > 0 {
   189  				token = p.tokens[len(p.tokens)-1]
   190  			}
   191  		}
   192  	}
   193  	var line, col int
   194  	if token != nil {
   195  		line = token.Line
   196  		col = token.Col
   197  	}
   198  	return &Error{
   199  		Template: p.template,
   200  		Filename: p.name,
   201  		Sender:   "parser",
   202  		Line:     line,
   203  		Column:   col,
   204  		Token:    token,
   205  		ErrorMsg: msg,
   206  	}
   207  }
   208  
   209  // Wraps all nodes between starting tag and "{% endtag %}" and provides
   210  // one simple interface to execute the wrapped nodes.
   211  // It returns a parser to process provided arguments to the tag.
   212  func (p *Parser) WrapUntilTag(names ...string) (*NodeWrapper, *Parser, *Error) {
   213  	wrapper := &NodeWrapper{}
   214  
   215  	tagArgs := make([]*Token, 0)
   216  
   217  	for p.Remaining() > 0 {
   218  		// New tag, check whether we have to stop wrapping here
   219  		if p.Peek(TokenSymbol, "{%") != nil {
   220  			tag_ident := p.PeekTypeN(1, TokenIdentifier)
   221  
   222  			if tag_ident != nil {
   223  				// We've found a (!) end-tag
   224  
   225  				found := false
   226  				for _, n := range names {
   227  					if tag_ident.Val == n {
   228  						found = true
   229  						break
   230  					}
   231  				}
   232  
   233  				// We only process the tag if we've found an end tag
   234  				if found {
   235  					// Okay, endtag found.
   236  					p.ConsumeN(2) // '{%' tagname
   237  
   238  					for {
   239  						if p.Match(TokenSymbol, "%}") != nil {
   240  							// Okay, end the wrapping here
   241  							wrapper.Endtag = tag_ident.Val
   242  							return wrapper, newParser(p.template.name, tagArgs, p.template), nil
   243  						} else {
   244  							t := p.Current()
   245  							p.Consume()
   246  							if t == nil {
   247  								return nil, nil, p.Error("Unexpected EOF.", p.last_token)
   248  							}
   249  							tagArgs = append(tagArgs, t)
   250  						}
   251  					}
   252  				}
   253  			}
   254  
   255  		}
   256  
   257  		// Otherwise process next element to be wrapped
   258  		node, err := p.parseDocElement()
   259  		if err != nil {
   260  			return nil, nil, err
   261  		}
   262  		wrapper.nodes = append(wrapper.nodes, node)
   263  	}
   264  
   265  	return nil, nil, p.Error(fmt.Sprintf("Unexpected EOF, expected tag %s.", strings.Join(names, " or ")),
   266  		p.last_token)
   267  }